============================== Sale Advance Payment Scenario ============================== Imports:: >>> import datetime as dt >>> from decimal import Decimal >>> from proteus import Model >>> from trytond.modules.account.tests.tools import ( ... create_chart, create_fiscalyear, get_accounts) >>> from trytond.modules.account_invoice.tests.tools import ( ... create_payment_term, set_fiscalyear_invoice_sequences) >>> from trytond.modules.company.tests.tools import create_company >>> from trytond.modules.sale_advance_payment.tests.tools import ( ... add_advance_payment_accounts, create_advance_payment_term) >>> from trytond.tests.tools import activate_modules, assertEqual >>> today = dt.date.today() >>> next_week = today + dt.timedelta(days=7) Activate sale_advance_payment:: >>> config = activate_modules( ... ['sale_advance_payment', 'sale_supply'], ... create_company, create_chart) Create fiscal years:: >>> fiscalyear = set_fiscalyear_invoice_sequences( ... create_fiscalyear(today=(today, next_week))) >>> fiscalyear.click('create_period') Get accounts:: >>> accounts = add_advance_payment_accounts(get_accounts()) >>> revenue = accounts['revenue'] >>> payable = accounts['payable'] >>> cash = accounts['cash'] >>> advance_payment_account = accounts['advance_payment'] >>> Journal = Model.get('account.journal') >>> PaymentMethod = Model.get('account.invoice.payment.method') >>> cash_journal, = Journal.find([('type', '=', 'cash')]) >>> cash_journal.save() >>> payment_method = PaymentMethod() >>> payment_method.name = 'Cash' >>> payment_method.journal = cash_journal >>> payment_method.credit_account = cash >>> payment_method.debit_account = cash >>> payment_method.save() Create customer:: >>> Party = Model.get('party.party') >>> customer = Party(name='Customer') >>> customer.save() Create account category:: >>> ProductCategory = Model.get('product.category') >>> account_category = ProductCategory(name="Account Category") >>> account_category.accounting = True >>> account_category.account_revenue = revenue >>> account_category.save() Create product:: >>> ProductUom = Model.get('product.uom') >>> ProductTemplate = Model.get('product.template') >>> unit, = ProductUom.find([('name', '=', 'Unit')]) >>> template = ProductTemplate() >>> template.name = 'Product' >>> template.default_uom = unit >>> template.type = 'goods' >>> template.salable = True >>> template.list_price = Decimal('20') >>> template.account_category = account_category >>> template.save() >>> product, = template.products Create payment term:: >>> payment_term = create_payment_term() >>> payment_term.save() Create an inventory:: >>> Inventory = Model.get('stock.inventory') >>> InventoryLine = Model.get('stock.inventory.line') >>> Location = Model.get('stock.location') >>> storage, = Location.find([ ... ('code', '=', 'STO'), ... ]) >>> inventory = Inventory() >>> inventory.location = storage >>> inventory_line = inventory.lines.new() >>> inventory_line.product = product >>> inventory_line.quantity = 100.0 >>> inventory_line.expected_quantity = 0.0 >>> inventory.click('confirm') >>> inventory.state 'done' Create advance payment term preventing the creation of shipment:: >>> advance_payment_term = create_advance_payment_term( ... 'Advance Payment', '0.1 * total_amount', advance_payment_account, ... block_supply=True) >>> advance_payment_term.save() Create a normal sale:: >>> Sale = Model.get('sale.sale') >>> sale = Sale() >>> sale.party = customer >>> sale.payment_term = payment_term >>> line = sale.lines.new() >>> line.product = product >>> line.quantity = 1 >>> sale.save() >>> sale.click('quote') >>> sale.click('confirm') >>> sale.click('process') >>> sale.state 'processing' As usual an invoice and a shipment has been created:: >>> invoice, = sale.invoices >>> invoice_line, = invoice.lines >>> assertEqual(invoice_line.account, revenue) >>> invoice.total_amount Decimal('20.00') >>> len(sale.shipments) 1 Create a sale with advance payment:: >>> sale = Sale() >>> sale.party = customer >>> sale.payment_term = payment_term >>> line = sale.lines.new() >>> line.product = product >>> line.quantity = 5 >>> sale.advance_payment_term = advance_payment_term >>> sale.click('quote') >>> line, = sale.advance_payment_lines >>> line.amount Decimal('10.00') >>> sale.click('confirm') >>> sale.click('process') >>> sale.state 'processing' The advance payment invoice has been created:: >>> invoice, = sale.advance_payment_invoices >>> invoice_line, = invoice.lines >>> assertEqual(invoice_line.account, advance_payment_account) >>> invoice.total_amount Decimal('10.00') >>> assertEqual(invoice.invoice_date, next_week) >>> invoice.invoice_date = None >>> invoice.click('post') >>> sale.reload() >>> len(sale.invoices) 0 >>> len(sale.shipments) 0 Let's pay the advance payment invoice:: >>> pay = invoice.click('pay') >>> pay.form.payment_method = payment_method >>> pay.execute('choice') >>> sale.reload() >>> sale.state 'processing' >>> len(sale.invoices) 1 >>> len(sale.shipments) 1 >>> invoice, = sale.invoices >>> invoice.total_amount Decimal('90.00') >>> len(invoice.lines) 2 >>> il1, il2 = sorted([il for il in invoice.lines], ... key=lambda il: 1 if il.product else 0) >>> assertEqual(il1.account, advance_payment_account) >>> il1.unit_price Decimal('10.00') >>> assertEqual(il1.taxes_date, today) >>> il1.quantity -1.0 >>> assertEqual(il2.product, product) >>> il2.unit_price Decimal('20.0000') >>> il2.quantity 5.0 Create another advance payment term preventing the packing stage:: >>> advance_payment_term_no_pack = create_advance_payment_term( ... 'Advance Payment (blocked packing)', ... '0.1 * total_amount', advance_payment_account, block_shipping=True) >>> advance_payment_term_no_pack.save() Create a sale with advance payment:: >>> sale = Sale() >>> sale.party = customer >>> sale.payment_term = payment_term >>> line = sale.lines.new() >>> line.product = product >>> line.quantity = 6 >>> sale.advance_payment_term = advance_payment_term_no_pack >>> sale.click('quote') >>> line, = sale.advance_payment_lines >>> line.amount Decimal('12.00') >>> sale.click('confirm') >>> sale.click('process') >>> sale.state 'processing' The shipment has been created:: >>> shipment, = sale.shipments Let's try to pack it:: >>> shipment.click('wait') >>> shipment.click('assign_try') >>> shipment.click('pick') >>> shipment.click('pack') Traceback (most recent call last): ... ShippingBlocked: ... Let's pay the advance payment invoice:: >>> invoice, = sale.advance_payment_invoices >>> assertEqual(invoice.invoice_date, next_week) >>> invoice.invoice_date = None >>> invoice.click('post') >>> pay = invoice.click('pay') >>> pay.form.payment_method = payment_method >>> pay.execute('choice') >>> sale.reload() >>> sale.state 'processing' Packing the shipment is now allowed:: >>> shipment.click('pack') >>> shipment.reload() >>> shipment.state 'packed' In case the product is to be supplied on sale --------------------------------------------- Create the product:: >>> sos_template = ProductTemplate() >>> sos_template.name = 'Supply On Sale product' >>> sos_template.default_uom = unit >>> sos_template.type = 'goods' >>> sos_template.purchasable = True >>> sos_template.salable = True >>> sos_template.list_price = Decimal('10') >>> sos_template.account_category = account_category >>> sos_template.supply_on_sale = 'always' >>> sos_template.save() >>> sos_product, = sos_template.products Sell 10 of those products:: >>> Sale = Model.get('sale.sale') >>> sale = Sale() >>> sale.party = customer >>> sale.payment_term = payment_term >>> sale_line = sale.lines.new() >>> sale_line.product = sos_product >>> sale_line.quantity = 10 >>> sale.advance_payment_term = advance_payment_term >>> sale.click('quote') >>> sale.click('confirm') >>> sale.click('process') >>> sale.state 'processing' There is no purchase request created yet:: >>> PurchaseRequest = Model.get('purchase.request') >>> PurchaseRequest.find() [] The advance payment invoice has been created, now pay it:: >>> invoice, = sale.advance_payment_invoices >>> assertEqual(invoice.invoice_date, next_week) >>> invoice.invoice_date = None >>> invoice.click('post') >>> pay = invoice.click('pay') >>> pay.form.payment_method = payment_method >>> pay.execute('choice') >>> sale.reload() >>> sale.state 'processing' There is now a purchase request of the desired quantity:: >>> purchase_request, = PurchaseRequest.find() >>> purchase_request.quantity 10.0 Testing advance payment lines exception handling ----------------------------------------------------- Create a sale with this term:: >>> sale = Sale() >>> sale.party = customer >>> sale.payment_term = payment_term >>> line = sale.lines.new() >>> line.product = product >>> line.quantity = 5 >>> sale.advance_payment_term = advance_payment_term >>> sale.click('quote') >>> line1, = sale.advance_payment_lines >>> line1.amount Decimal('10.00') >>> sale.click('confirm') >>> sale.click('process') >>> sale.state 'processing' Let's cancel the advance invoice:: >>> inv, = sale.advance_payment_invoices >>> inv.click('cancel') >>> sale.reload() >>> sale.invoice_state 'exception' Handle the exception on the sale level, not recreating the invoice will create the final invoice with the remaining total:: >>> handle_exception = sale.click('handle_invoice_exception') >>> handle_exception.form.ignore_invoices.extend( ... handle_exception.form.ignore_invoices.find()) >>> handle_exception.execute('handle') >>> sale.reload() >>> len(sale.advance_payment_invoices) 1 >>> last_invoice, = sale.invoices >>> last_invoice.total_amount Decimal('100.00') Let's now use the same scenario but recreating the invoice instead of ignoring it:: >>> sale = Sale() >>> sale.party = customer >>> sale.payment_term = payment_term >>> line = sale.lines.new() >>> line.product = product >>> line.quantity = 5 >>> sale.advance_payment_term = advance_payment_term >>> sale.save() >>> sale.click('quote') >>> sale.click('confirm') >>> sale.click('process') >>> sale.state 'processing' >>> inv, = sale.advance_payment_invoices >>> inv.click('cancel') >>> sale.reload() >>> sale.invoice_state 'exception' >>> handle_exception = sale.click('handle_invoice_exception') >>> handle_exception.form.recreate_invoices.extend( ... handle_exception.form.recreate_invoices.find()) >>> handle_exception.execute('handle') >>> sale.reload() >>> _, inv_recreated = sale.advance_payment_invoices >>> inv_recreated.total_amount Decimal('10.00') >>> assertEqual(inv_recreated.invoice_date, next_week) >>> inv_recreated.invoice_date = None >>> inv_recreated.click('post') >>> pay = inv_recreated.click('pay') >>> pay.form.payment_method = payment_method >>> pay.execute('choice') >>> sale.reload() >>> last_invoice, = sale.invoices >>> last_invoice.total_amount Decimal('90.00')