custom discount applied twice in magento 2 - magento2

I have applied custom discount in magento 2.1 using this. Everything is working on local system but when I deploy the changes on server, discount get subtracted twice from the total.
Can any one any idea about this?
Sales.xml
<?xml version="1.0"?><config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Sales:etc/sales.xsd">
<section name="quote">
<group name="totals">
<item name="test_discount" instance="Namespace\Modulename\Model\Quote\Discount" sort_order="500"/>
</group>
</section>

To remove the custom discount applying twice in your summary .Just add below Lines
$items = $shippingAssignment->getItems();
if (!count($items)) {
return $this;
}
in the collect method. Once done, it will look like below.
public function collect(
\Magento\Quote\Model\Quote $quote,
\Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment,
\Magento\Quote\Model\Quote\Address\Total $total
) {
$items = $shippingAssignment->getItems();
if (!count($items)) {
return $this;
}
parent::collect($quote, $shippingAssignment, $total);

This could be because of multishipping.
Magento collects totals per address. In the default scenario a quote has two addresses: A billing address and a shipping address.
I have not tried the example you linked, but afaiks the fee will be applied twice. For some reason this does not show up on the frontend, but I also had problems where a fee was applied twice, but somehow still showed up correctly on the frontend.
I assume when having a multiple shipping order, there is still only one billing address. Therefore the following snippet should fix the issue of having custom fees/discounts applied twice:
$address = $shippingAssignment->getShipping()->getAddress();
if($address->getAddressType() != 'billing'){
return $this;
}
Or, if you only want to apply the fee/charge for shipments with items:
$allBaseTotals = $total->getAllBaseTotalAmounts();
if(!isset($allBaseTotals['subtotal']) || $allBaseTotals['subtotal'] <= 0){
return $this;
}
This means the example file in the tutorial you linked would turn out like this:
app/code/Magestore/Webpos/Model/Total/Quote/Custom.php
/**
* #param \Magento\Quote\Model\Quote $quote
* #param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment
* #param \Magento\Quote\Model\Quote\Address\Total $total
* #return $this|bool
*/
public function collect(
\Magento\Quote\Model\Quote $quote,
\Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment,
\Magento\Quote\Model\Quote\Address\Total $total
)
{
parent::collect($quote, $shippingAssignment, $total);
$address = $shippingAssignment->getShipping()->getAddress();
if($address->getAddressType() != 'billing'){
return $this;
}
$baseDiscount = 10;
$discount = $this->_priceCurrency->convert($baseDiscount);
$total->addTotalAmount('customdiscount', -$discount);
$total->addBaseTotalAmount('customdiscount', -$baseDiscount);
$total->setBaseGrandTotal($total->getBaseGrandTotal() - $baseDiscount);
$quote->setCustomDiscount(-$discount);
return $this;
}
Edit:
It seems if($address->getAddressType() != 'billing'){ does not work as I thought it would.
I'm using if($address->getAddressType() == 'billing'){ for now as I'm not using multishipping.

Discount is calculated by address in quote, since there are two address billing and shipping that why is calculated twice.
you can set condition on either billing or shipping that only calculate for shipping address or billing address.
here just put this simple if statement (from nbjohan answer) at the start.
$address = $shippingAssignment->getShipping()->getAddress();
if($address->getAddressType() != 'billing'){
return $this;
}
now it will calculate only for one time.

Related

Contentsliding is not stopping at sysfolder since TYPO3 v10 - How to solve?

The TSref entry for slide explains:
Up to Version 9 of TYPO3 the sliding stopped when reaching a folder.
Beginning with TYPO3 10 this is not longer the case. See
$cObj->checkPid_badDoktypeList.
Ok, this variable is still 255 (formerly directly, now via constant PageRepository::DOKTYPE_RECYCLER).
What exactly should I see there that will help me? Or better, how to get content sliding still working like before?
You have to extend the ContentObjectRenderer class and overwrite the getSlidePids method with your own extension.
In the boot function of ext_localconf.php:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class] = [
'className' => \YourVendor\YourExtensionKey\ContentObject\ContentObjectRenderer::class
];
Then you have to create your own "Classes/ContentObject/ContentObjectRenderer.php" with:
<?php
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace YourVendor\YourExtension\ContentObject;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class ContentObjectRenderer extends \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
{
/**
* Returns all parents of the given PID (Page UID) list
*
* #param string $pidList A list of page Content-Element PIDs (Page UIDs) / stdWrap
* #param array $pidConf stdWrap array for the list
* #return string A list of PIDs
* #internal
*/
public function getSlidePids($pidList, $pidConf)
{
// todo: phpstan states that $pidConf always exists and is not nullable. At the moment, this is a false positive
// as null can be passed into this method via $pidConf. As soon as more strict types are used, this isset
// check must be replaced with a more appropriate check like empty or count.
$pidList = isset($pidConf) ? trim($this->stdWrap($pidList, $pidConf)) : trim($pidList);
if ($pidList === '') {
$pidList = 'this';
}
$tsfe = $this->getTypoScriptFrontendController();
$listArr = null;
if (trim($pidList)) {
$listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$tsfe->contentPid, $pidList));
$listArr = $this->checkPidArray($listArr);
}
$pidList = [];
if (is_array($listArr) && !empty($listArr)) {
foreach ($listArr as $uid) {
$page = $tsfe->sys_page->getPage($uid);
if($page['doktype'] == PageRepository::DOKTYPE_SYSFOLDER)
break;
if (!$page['is_siteroot']) {
$pidList[] = $page['pid'];
}
}
}
return implode(',', $pidList);
}
}

How to add notice on magento 2 checkout, on street address field for billing and shipping

I want to add notice on street address field, on magento 2 checkout page. I tried adding it in: using layout process
<?php
namespace Pawan\Notice\Model\Checkout;
class LayoutProcessorPlugin
{
/**
* #param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
* #param array $jsLayout
* #return array
*/
public function afterProcess(
\Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
array $jsLayout
) {
$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
['shippingAddress']['children']['shipping-address-fieldset']['children']['street']['children'][1]
['notice'] = __('This is my custom notice.');
$jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']
['billingAddress']['children']['billing-address-fieldset']['children']['street']['children'][1]
['notice'] = __('This is my custom notice.');
return $jsLayout;
}
}
it added only for shipping address but not billing can any one tell me how to add my billing address section under payment method
If need find exact place of tags just print layout process and add notice to particular tag
$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/extranotice.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('Simple Text Log'); // Simple Text Log
$logger->info('Array Log $jsLayout'.print_r($jsLayout, true)); // Array Log
checked on log file based on that I added for street
$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
['shippingAddress']['children']['shipping-address-fieldset']['children']['street']['children'][0]
['notice'] = __('No PO Box');

Custom shipping calculator which takes destination into account

I am trying to create a custom calculator which calculates shipping costs based of a deleivery address. For now, I will be hardcoding different fees according to a postcode prefix... e.g.
SK1 = 4
SK2 = 4
SK3 = 4
SK4 = 4
M1 = 6
M2 = 6
M3 = 6
M4 = 6
Everything else = 10
I am following the tutorial here.
The code stub I have is as follows:
<?php
/**
* Created by IntelliJ IDEA.
* User: camerona
* Date: 03/03/2017
* Time: 08:09
*/
namespace AppBundle\Shipping;
use Sylius\Component\Shipping\Calculator\CalculatorInterface;
use Sylius\Component\Shipping\Model\ShippingSubjectInterface;
class PostCodeCalculator implements CalculatorInterface
{
public function calculate(ShippingSubjectInterface $subject, array $configuration)
{
return $this->postCodeService->getShippingCostForPostCode($subject->getShippingAddress());
}
public function getType()
{
// TODO: Implement getType() method.
}
}
Is there a way in sylius where I can get access to the shipping address of an order? The ShippingSubjectInterface only allows access to volume, weight, items, and shippables.
/** #var $subject Shipment */
$postCode = $subject->getOrder()->getShippingAddress()->getPostCode();
Allowed me get address from subject.

Statistic Calculations in laravel

I'm working on a project for golfstatistics. Right now I made it so far to enter and edit golfstatistics. I'm working with laravel 5 btw.
My database schema works like this:
Every round you play saves one entry in the round table with information like (date, playid, weather, courseid)
for every hole played an entry in the score table is made. There I have a reference to the roundid and information like (score, fairwayhit, greenhit, putts, penalties, ....)
No I want to create reports where I can filter for date and course etc.
What I did for now is. I created a Statistic class where I can pass the date, playerid, roundid, courseid in the construct. The construct will query all the rounds played, matching those filters.
Then, foreach statistic I made a public function ex. scoring_average, greenhit_percantage, putts_per_round, putts_per_greeninregulation etc. there are about 15 stats.
So my question is: is that right what I'm doing here? because I have about 15 functions just to calculate statistics.
Please give me some advice if you have a better solution.
Thank you
class Statistic {
/**
* The table associated with the model.
*
* #var string
*/
public $rounds = [];
public function __construct($user_id, $roundid = null, $start = "2000-01-01", $end = "2030-01-01", $courseid = 0){
$this->rounds = Round::where('user_id', '=', $user_id)->get();
}
public function score(){
if(count($this->rounds) > 0){
$avg = 0;
foreach($this->rounds as $round){
$scores = Score::where('round_id', '=', $round->id)->get(['score']);
foreach($scores as $score){
$avg += $score->score;
}
}
return $avg / count($this->rounds);
} else {
return "N/A";
}
}
public function fir(){
if(count($this->rounds) > 0){
$fairway = [];
foreach($this->rounds as $round){
$scores = Score::where('round_id', '=', $round->id)->get(['fir']);
foreach($scores as $score){
if($score->fir != 0){
array_push($fairway, $score->fir);
}
}
}
$hits = array_count_values($fairway);
//unset($hits[0]); //unsets par 3 with value 0
return self::percArray($hits);
return $perc;
} else {
return "N/A";
}
}

How to set either Zend_Form_Element_Text required?

I'm new to Zend Framework, I have a question is that if I have two Zend_Form_Element_Text in a form, and I want make either of them to be filled by the user.
For example, phone number and mobile number. People only need enter one of them to continue.
How am I going to do this? Thanks
Hi i think you can do the following.
If you're initalizing your form in an Controller Action try this:
/**
* IndexAction
*
* #return void
*/
public function indexAction() {
// initalize your form
$form = new MyForm();
// get post data
$post = $this->_request->getPost();
// check if phone number is empty
if (! empty($post['phone_number'])) {
// remove validator from mobile phone element
$mobile = $form->getElement('mobile');
$mobile->removeValidator('notEmpty');
$mobile->setRequired(false);
}
if ($form->isValid($this->getRequest()->getPost())) {
$input = $form->getValues();
// do something with the input
print_r($input, true);
}
}