COD & auto-payment issue

195 views
Skip to first unread message

David Unric

unread,
Jan 2, 2013, 4:17:08 AM1/2/13
to satchm...@googlegroups.com
Hello,

can anybody explain why Satchmo makes a payment of an order at checkout process when Cash On Delivery payment method is selected ? I did believed COD payment means payment is made at the moment of delivery not in an advance.
I did tried to find some option through livesetting interface, even took a peek in cod module sources but found nothing particular.

So how to tweak the COD payment method so order is _not_ automatically paid and order is left with full due ballance, also shown in generated invoice ?

Thanks.

David Unric

unread,
Jan 2, 2013, 10:13:02 AM1/2/13
to satchm...@googlegroups.com
After reading docs at satchmoproject site it looks like the concept of payment methods handles only two cases:
  1) if payment module returns True (i.e. "success") it generates payment for the whole outstanding sum, no matter what so it is in contradiction to C.O.D.
  2) if payment module returns False, the checkout process can not be completed, error page about unfinished payment is rendered

So is there some workaround to complete the checkout without order being paid or the checkout process has to be tweaked/rewritten ? That looks like a lot of work, in addition to keep with Satchmo's updates.

Stanislav Mihaylov

unread,
Jan 13, 2013, 10:20:17 AM1/13/13
to satchm...@googlegroups.com
I am sorry, but I seem not to understand what exactly is the problem here:

The COD order payment records that you see in the admin are clearly indicating that it is COD with balance of 0 - meaning you haven't received any payment.
The generated invoices from the 'Product Orders' section have the full due amount calculated for the customer:

Subtotal: £30.57
Shipment charge: £5.00
Tax: £6.11
Total: £41.68
Payments Made - £0.00
Balance Due £41.69

Payment total £41.68 due to:
Accounts Receivable
...some address

Later on when you have received the payment you can manually change the amount in the corresponding 'Order Payment' record and then the generated invoice will be indicated as 'Paid' with 0.00 due amount:

Subtotal: £30.57
Shipment charge: £5.00
Tax: £6.11
Total: £41.68
Payments Made - £41.69
Balance Due £0.00
This invoice has been paid in full. Thank you for your business!

David Unric

unread,
Jan 19, 2013, 8:39:02 PM1/19/13
to satchm...@googlegroups.com
Thanks for the answer, Stanislav. I started to believe nobody would reply.

Let's show on an example what I mean:

At checkout process - confirmation, there is following ballance:

Items Ordered

Python Rocks shirt - $19.50 x 1 = $19.50
...

Total

Subtotal = $19.50
Shipping + $4.00
Total = $23.50

Payment

This order will be COD for $23.50.

So far, so good. But after order confirmation by submitting "Purchase Items" button:

Paid

13-Jan-19 17:24 Cod payment - $23.50
Balance = $0.00
                 ^^^^^^^ no payment made but balance was cleared

In Order Payments, there is a record for realized payment, the auto-generated invoice does contain record with "Payments Made -$23.50" and "Balance due $0.00" and "This invoice has been paid in full." and that simply is _not_ true !
The payment will be realized not until the moment of delivery, ie. Cash will be collected On Delivery.

So the question remains - how to prevent of automatic payment at the end of Checkout process when COD type of payment is selected ?

Stanislav Mihaylov

unread,
Jan 25, 2013, 3:03:19 PM1/25/13
to satchm...@googlegroups.com
Oh, I got it now...my bad, I was looking at records of Orders that were not confirmed (right after checkout step 2), which in fact should not even exist in the database records if they are not confirmed...weird.
In order to fix this issue I had to unfortunately modify the core files of Satchmo, so the following code is a suggestion that you can take on your own risk of having some other functionality broken or not being able to update Satchmo with newer versions.

Basically I decided to remove the creation of 'pending_payment' at step 2 and instead create pending payment at the last step 3, simultaneously removing the 'self.record_payment' line from processor.py of COD. Nevertheless, the orders are still being saved in the database after step 2, otherwise I received bunch of errors. You can still tweak the 'status' of orders though, so I managed to define orders after step 2 with status 'Temp' and after confirmation with status 'New'.

So here are the files affected:

/payment/forms.py
class SimplePayShipForm, method save()

def save(self, request, cart, contact, payment_module, data=None):
        form_presave.send(SimplePayShipForm, form=self)
        if data is None:
            data = self.cleaned_data
        self.order = get_or_create_order(request, cart, contact, data)
        if payment_module and payment_module.KEY.value != "COD":   #this is the modified line
            processor_module = payment_module.MODULE.load_module('processor')
            processor = processor_module.PaymentProcessor(payment_module)
            self.orderpayment = processor.create_pending_payment(order=self.order)
        else:
            self.orderpayment = None
        form_postsave.send(SimplePayShipForm, form=self)

Also in the same file, class PaymentContactInfoForm, method save() you can add after self.order = get_or_create_order(request, cart, contact, self.cleaned_data) to define the 'Temp' status:
self.order.add_status(status='Temp', notes = "This order has not been confirmed yet")

/payment/views/confirm.py class ConfirmController:

def _onSuccess(self, controller):
        """Handles a success in payment.  If the order is paid-off, sends success, else return page to pay remaining."""
        if controller.paymentModule.KEY.value == "COD" or controller.order.paid_in_full:  #modified line
            controller.cart.empty()
            for item in controller.order.orderitem_set.all():
                if item.product.is_subscription:
                    item.completed = True
                    item.save()
            try:
                curr_status = controller.order.orderstatus_set.latest()  
            except OrderStatus.DoesNotExist:
                curr_status = None
                
            if (curr_status is None) or (curr_status.notes and curr_status.status == "New") or (curr_status.notes and curr_status.status == "Temp"): #modified line
                controller.order.add_status(status='New', notes = "Order successfully submitted")            
            else:
                # otherwise just update and save
                if not curr_status.notes:
                    curr_status.notes = _("Order successfully submitted")
                curr_status.save()                

            #Redirect to the success page
            url = controller.lookup_url('satchmo_checkout-success')
            return HttpResponseRedirect(url)    

        else:
            log.debug('Order #%i not paid in full, sending to pay rest of balance', controller.order.id)
            url = controller.order.get_balance_remaining_url()
            return HttpResponseRedirect(url)

payment/utils.py

def get_or_create_order(request, working_cart, contact, data):
    """Get the existing order from the session, else create using
    the working_cart, contact and data"""
    shipping = data.get('shipping', None)
    discount = data.get('discount', None)
    notes = data.get('notes', None)

    try:
        order = Order.objects.from_request(request)
        if order.status != '' and order.status != 'Temp':  #modified line
            # This order is being processed. We should not touch it!
            order = None
    except Order.DoesNotExist:
        order = None

    update = bool(order)
    if order:
        # make sure to copy/update addresses - they may have changed
        order.copy_addresses()
        order.save()
        if discount is None and order.discount_code:
            discount = order.discount_code
    else:
        # Create a new order.
        order = Order(contact=contact)

    pay_ship_save(order, working_cart, contact,
        shipping=shipping, discount=discount, notes=notes, update=update)
    request.session['orderID'] = order.id
    return order

/payment/modules/cod/processor.py

def capture_payment(self, testing=False, order=None, amount=None):
        """
        COD is always successful.
        """
        if not order:
            order = self.order

        if amount is None:
            amount = order.balance

        payment = self.create_pending_payment(order, amount)
        #payment = self.record_payment(order=order, amount=amount,
        #    transaction_id="COD", reason_code='0')

        return ProcessorResult(self.key, True, _('COD Pending'), payment)

This is all I did I believe, I hope it could guide you whatever you decide to do!

Stanislav Mihaylov

unread,
Jan 26, 2013, 12:58:13 PM1/26/13
to satchm...@googlegroups.com
Actually you can ignore the logic about changing order status to 'Temp' and then 'New', because by default the status before confirmation is None - displayed as (None) and after confirmation gets to 'New'. So you can just leave it like that. I am currently wondering if there is any cron job to delete every order with status of None that has a time stamp older than e.g. 24h. If you find anything like that, please share it.

David Unric

unread,
Jan 31, 2013, 7:19:36 AM1/31/13
to satchm...@googlegroups.com
Hi,

I've made modified Satchmo sources by your instructions but unfortunatelly checkout process does not finish, it stops with "To complete this order, you need to pay the $xx.xx balance remaining on this order." .

Probably have to wait until half-baked checkout/payment code will be fixed & tested in an upstream.
I'm forced the custom shop I'm working on to do in Spree/Rails now, although I'm more familiar with Python and its library. Next time, maybe.

Stanislav Mihaylov

unread,
Jan 31, 2013, 4:50:18 PM1/31/13
to satchm...@googlegroups.com
Yep, unfortunately I noticed the same problem, so I ended up returning it to the original source code and rather modifying the view that is connected to the templates to display a different message when the payment module is COD. That's the best way I could think of, but in the admin records the order is still displayed with completed payment and status 'New'. Good luck with Rails!

David Unric

unread,
Feb 2, 2013, 2:12:48 PM2/2/13
to satchm...@googlegroups.com
No problem, fixing this issue was too hacky anyway - too many incompatible changes, even too specific. Imagine another payment modules requiring such or similar functionality. Hope there will be some generic consistent solution in upcoming Satchmo versions.

Rails/Spree look like more mature product. In comparison you can simply define your own payment methods with or without delayed payment just with web admin interface, ie. not writing a simple line of code ! It was very easy to launch a basic custom project, there are support modules/controllers for everything you can imagine. On the other hand in comparison with Satchmo/Django it's a bit more 'heavier' on system resources and Ruby although a great language I find it a bit hairy with many special cases. It also lacks the unambiguousness and straightforwardness of Python.

Toni Mueller

unread,
Feb 13, 2013, 1:03:39 PM2/13/13
to satchm...@googlegroups.com

Hi Stanislav,

On Sun, Jan 13, 2013 at 07:20:17AM -0800, Stanislav Mihaylov wrote:
> The generated invoices from the 'Product Orders' section have the full due
> amount calculated for the customer:
>
> *Subtotal: �30.57*
> *Shipment charge: �5.00*
> *Tax: �6.11*
> *Total: �41.68*
> *Payments Made - �0.00*
> *Balance Due �41.69*

I hope you didn't take this example from a real-life shop?

Otherwise, it would illustrate a disturbing rounding error in Satchmo.


Kind regards,
--Toni++

Stanislav Mihaylov

unread,
Feb 13, 2013, 6:10:09 PM2/13/13
to satchm...@googlegroups.com, toni.m...@oeko.net
Well, you are right, I have already corrected this rounding error, which was a result of wrong VAT calculation methods. Since I have done a lot of changes in the core of Satchmo I cannot be 100% sure that the problems lies within its percent tax module alone or it's rather a more complex thing, but the way taxes were calculated for each newly created order and cart is disturbing. If we take the general case of 20% VAT, the price of single unit with tax is the original price, multiplied by 1.2 and then rounded to 2 decimal places. So far so good. But the way of calculating the tax of a line of units or the whole order follows exactly the same logic - it sums up all the prices first and then multiplies the sum by 1.2 and then rounds the result, when in fact the way to do it is to calculate each single unit taxed price separately, round it and then sum up all taxed prices. So that's where the difference comes from. E.g: a product costs $1.88, it's single taxed price is 2.256, rounded to 2.26. But if we have two products 2 x 2.256 = 4.512 and 2 x 2.26 = 4.52. It took me a while to fix it, this definitely needs serious improvements! The function 'by_orderitem' of the processor of the tax percent module illustrates this behaviour.

On Wednesday, February 13, 2013 6:03:39 PM UTC, Toni Mueller wrote:

Hi Stanislav,

On Sun, Jan 13, 2013 at 07:20:17AM -0800, Stanislav Mihaylov wrote:
> The generated invoices from the 'Product Orders' section have the full due
> amount calculated for the customer:
>
> *Subtotal: �30.57*
> *Shipment charge: �5.00*
> *Tax: �6.11*
> *Total: �41.68*
> *Payments Made - �0.00*
> *Balance Due �41.69*

Olivier Lauret

unread,
Feb 14, 2013, 2:50:50 AM2/14/13
to satchm...@googlegroups.com, toni.m...@oeko.net
Hi Stanislav,

Is this change already in satchmo? I didn't see it in the change/commit log.

Regards,
Olivier

--
You received this message because you are subscribed to the Google Groups "Satchmo users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to satchmo-user...@googlegroups.com.
To post to this group, send email to satchm...@googlegroups.com.
Visit this group at http://groups.google.com/group/satchmo-users?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

David Unric

unread,
Feb 17, 2013, 6:31:55 AM2/17/13
to satchm...@googlegroups.com
Hi Stanislav,

as I've got some spare time I did tried to reimplement your changes to get a pending (COD) payment and it does work as promised !
Are You working on this functionality and plan to update the upstream ? I can clean the code a bit, handle it by new a configuration setting in livesettings etc.



On Wednesday, January 2, 2013 10:17:08 AM UTC+1, David Unric wrote:
Reply all
Reply to author
Forward
0 new messages