Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update of delivery methods #838

Open
markusmo opened this issue Nov 18, 2020 · 6 comments
Open

Update of delivery methods #838

markusmo opened this issue Nov 18, 2020 · 6 comments

Comments

@markusmo
Copy link

markusmo commented Nov 18, 2020

Hi
I have a delivery method, which is only valid if a customer is living in a certain area in which she/he lives in.

 class ClimateNeutralShippingModifier(ShippingModifier):
     identifier = 'climate-neutral-shipping'

     def get_choice(self):
         return (self.identifier, _("Climate neutral shipping"))

     def is_disabled(self, cart):
         # geolocate address of customer
         if cart.shipping_address:
             postal_code = cart.shipping_address.postal_code
             city = cart.shipping_address.city
             country = cart.shipping_address.country
             distance = checkdistance(postal_code, city, country)
             if distance > settings.SHIPPING_DISTANCE:
                 return True
         return False

     def add_extra_cart_row(self, cart, request):
         if not self.is_active(cart.extra.get('shipping_modifier')) and len(cart_modifiers_pool.get_shipping_modifiers()) > 1:
             return
         amount = Money(settings.BIKING_PRICE)
         instance = {'label': _("Shipping costs"), 'amount': amount}
         cart.extra_rows[self.identifier] = ExtraCartRow(instance)
         cart.total += amount

     def ship_the_goods(self, delivery):
         super().ship_the_goods(delivery)

But somehow it will not be disabled, if distance is greater than my configured shipping distance. And now in my production instance I am getting errors like:
Bildschirmfoto 2020-11-18 um 20 58 18
But it should not even be a possibilty to select anyways... This error always occurs for the first shipping. After that this error does not occur anymore.
I guess it has to do something with saving shipping address to a customer... but I do not really get any other error than WARNING: Forbidden: /shop/api/shipping_address/add

Any suggestions how to either adapt the error message or check for distance in another way?

@ay0000
Copy link

ay0000 commented Mar 1, 2021

Did you ever figure this out?

@markusmo
Copy link
Author

markusmo commented Mar 1, 2021

Did you ever figure this out?

Sadly I think this still occurs ... Do you have the same or a similar issue?

@ay0000
Copy link

ay0000 commented Mar 1, 2021

Yes, kind of. I’m looking to modify the label of the modifier depending on the country selected. It works fine if you select a single country and don’t change it. Even if you do change it, the prices will update on the backend but the label on the site will not.

@markusmo
Copy link
Author

markusmo commented Mar 5, 2021

As for now, I precalculated possible values ... but that only helps temporary ... I will let you know, if I can come up with something.

@ay0000
Copy link

ay0000 commented Mar 5, 2021

So I think that I have a workaround for at least changing the page display...you may be able to use it in order to just hide the limited delivery option that you're trying to implement...it's a little messy and it sometimes loses synchronization but bear with me.

In each one of my modifiers, I made and instantiated an handler class with a getService() function that I can call using AJAX from the clicking the NEXT button or numbered steps at the top of the page that contains the service name.

class AjaxHandler():
    def __init__(self):
        self.friendlyService=''

    def updateService(self,name):
        print(self.friendlyService)
        self.friendlyService=name
        return

    def getService(self,request):
        print((dir(request.POST)),flush=True)
        print((request.POST['tag'][0]),flush=True)
        print(self.friendlyService)
        data={request.POST['tag']:self.friendlyService}
        return JsonResponse(data)

Then I overrode the pre_process_cart() function that it inherits from BaseModifier. Unfortunately that function will run on every single step of the checkout process, even before any address info has been entered, instead of just at the point where the modifier is activated (i.e. after the address has been entered and on the payment/shipping page). This requires some checking in order to ensure that you're not trying to do anything with address information before it's even available...which'll crash the checkout process. This is also the reason for the default values at the top of the class (i.e. shippingCost, postCode, etc.,)

class CanadaPostExpeditedShippingModifier(ShippingModifier):
    """
    This is just a demo on how to implement a shipping modifier, which when selected
    by the customer, adds an additional charge to the cart.
    """
    identifier = 'canpost-expedited'
    service_friendlyName=''
    shippingCost='15'
    postCode=''
    ajaxHandler=AjaxHandler()
    service=0

    currentCountry=None
    currentZip=None

    def pre_process_cart(self, cart, request, raise_exception=False):
        if (hasattr(cart, "shipping_address")):
            if (hasattr(cart.shipping_address, "country")):
                print(cart.shipping_address.country)
                if (self.currentCountry == None or cart.shipping_address.country != self.currentCountry):
                    if(cart.shipping_address.country=='CA' or cart.shipping_address.country=='US'):
                        if(cart.shipping_address.zip_code!=self.currentZip):
                            print("Zip Changed")
                            print(self.shippingCost)
                            self.service_friendlyName, self.shippingCost = cpcOps.getShippingCost(
                                (cart.shipping_address.country,
                                 cart.shipping_address.zip_code),
                                service=self.service)
                            self.ajaxHandler.friendlyService = "{}-{}".format(self.service_friendlyName,
                                                                              self.shippingCost)
                            self.currentZip = cart.shipping_address.zip_code
                            print(self.ajaxHandler.friendlyService)
                    else:
                        print("Country Changed")
                        print(self.shippingCost)
                        self.service_friendlyName, self.shippingCost = cpcOps.getShippingCost(
                            (cart.shipping_address.country,
                             cart.shipping_address.zip_code),
                            service=self.service)
                        self.ajaxHandler.updateService("{}-{}".format(self.service_friendlyName, self.shippingCost))
                        self.currentCountry = cart.shipping_address.country
                        print(self.ajaxHandler.friendlyService)
                else:
                    print("No location change detected")
        return

From there, you can create a checkout folder in your templates folder and copy and paste the shipping-method-form.html, next-step-button.html, process-bar.html, etc., and modify them to your liking. For instance, I needed to break out each individual option that I had so I got rid of the {{shipping_modifier.as_div}} and replaced it with a modified version of the template that it generates.

<script>
function nextAjax() {

		var labelUrlDict={'canpost-regular': '{% url "updateRegShip" %}',
						'canpost-priority': '{% url "updatePriShip" %}',
						'canpost-expedited': '{% url "updateExpShip" %}',
						'canpost-xpressPost': '{% url "updateXprShip" %}'
		}
		var labelTagDict={'canpost-regular': 'label_shipping_method_form-shipping_modifier_2',
						'canpost-priority': 'label_shipping_method_form-shipping_modifier_1',
						'canpost-expedited': 'label_shipping_method_form-shipping_modifier_0',
						'canpost-xpressPost': 'label_shipping_method_form-shipping_modifier_3'
		}


		for(var serviceId in labelUrlDict){
			$.ajax({
			url: labelUrlDict[serviceId],
			type: "POST",
			dataType: 'json',
			data:{'csrfmiddlewaretoken':'{{ csrf_token }}','tag':labelTagDict[serviceId]},
			beforeSend: function() {
    			$('#loader').show();
    			console.log("AJAX Started");
  			},
  			complete: function(){
    			 $('#loader').hide();
    			 console.log("AJAX Done");
  			},
			success: function (data) {
			  if (data) {

				console.log(Object.entries(data));
				$("span[name="+Object.entries(data)[0][0]+"]").text(Object.entries(data)[0][1]);
			  }
			}
		  });

	  	}

}
</script>
{% endaddtoblock %}

...

<form shop-method-form djng-endpoint="{% url 'shop:checkout-upload' %}" name="{{ shipping_method_form.form_name }}" class="mt-3" novalidate>
{% if shipping_method_form.has_choices %}
<div class="djng-line-spreader">
	<ul ng-show="shipping_method_form.$pristine" class="djng-form-errors" ng-cloak>
		<li ng-show="shipping_method_form.$error.rejected && shipping_method_form.$message" class="invalid" ng-bind="shipping_method_form.$message"></li>
	</ul>
</div>
<div class="form-group has-feedback djng-field-required">
	<div id="shipping_method_form-shipping_modifier">
	{% for choice in shipping_method_form.shipping_modifier %}
	<div class="radio">
		<label>
			{{choice.tag}}
			<span class="label" name="label_shipping_method_form-shipping_modifier_{{ forloop.counter0 }}">
				{{choice.choice_label}}
			</span>
		</label>
	</div>
	{% endfor %}
	</div>
	<ul ng-show="shipping_method_form['shipping_modifier'].$dirty && !shipping_method_form['shipping_modifier'].$untouched" class="djng-form-control-feedback djng-field-errors" ng-cloak>
		<li ng-show="shipping_method_form['shipping_modifier'].$error.required" class="invalid">At least one radio button has to be selected.</li>
		<li ng-show="shipping_method_form['shipping_modifier'].$valid" class="valid"></li>
	</ul>
	<ul ng-show="shipping_method_form['shipping_modifier'].$pristine" class="djng-form-control-feedback djng-field-errors" ng-cloak>
		<li ng-show="shipping_method_form['shipping_modifier'].$valid" class="valid"></li>
		<li ng-show="shipping_method_form['shipping_modifier'].$error.rejected && shipping_method_form['shipping_modifier'].$message" class="invalid" ng-bind="shipping_method_form['shipping_modifier'].$message"></li>
	</ul>
	<input type="hidden" name="plugin_id" value="1043" ng-model="shipping_method['plugin_id']" class="form-control" id="shipping_method_form-plugin_id">
	<input type="hidden" name="plugin_order" value="5" ng-model="shipping_method['plugin_order']" class="form-control" id="shipping_method_form-plugin_order">
</div>
	{% if show_additional_charge %}
	<ul class="additional-charge">
		{% for choice, label in shipping_method_form.shipping_modifier.field.choices %}
		<li ng-repeat="extra_row in cart.extra_rows|filter:{modifier:'{{ choice }}'}">{% trans "Additional charge: {{ extra_row.amount }}" context "checkout" %}</li>
		{% endfor %}
		{% if shipping_modifiers.initial_row %}
		<li ng-if="!cart.extra_rows">{% blocktrans with amount=shipping_modifiers.initial_row.amount context "checkout" %}Additional charge: {{ amount }}{% endblocktrans %}</li>
		{% endif %}
	</ul>
	{% endif %}
{% else %}
	<input name="__force_invalidation__" style="display: none;" required />
	<p class="bg-danger">{% trans "No shipping method available" context "checkout" %}</p>
{% endif %}
</form>

I also modified the next-step-button.html file to run the nextAjax() on every click in order to make sure that the information being provided to/by the Python AJAX handler is current.

<button **onclick="nextAjax()"** ng-click="do(prepare()).then(update()).then(showOK()).then(emit('shop.checkout.digest')).then(delay(333)).then(nextStep).catch(scrollToRejected()).finally(restore())

I'm not sure if any of this helps you but you may be able to use the AJAX call to just outright hide the shipping modifier from view instead of changing its name. Let me know what you think.

@markusmo
Copy link
Author

markusmo commented Mar 7, 2021

Thanks for the extensive research. I wil have a look at it, and will come back to you. It looks promising.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants