================= Purchase Scenario ================= Imports:: >>> import datetime as dt >>> from decimal import Decimal >>> from proteus import Model, Report >>> from trytond.modules.account.tests.tools import ( ... create_chart, create_fiscalyear, create_tax, 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.tests.tools import activate_modules, assertEqual, set_user >>> today = dt.date.today() Activate modules:: >>> config = activate_modules('purchase', create_company, create_chart) >>> Employee = Model.get('company.employee') >>> Party = Model.get('party.party') >>> User = Model.get('res.user') Set employee:: >>> employee_party = Party(name="Employee") >>> employee_party.save() >>> employee = Employee(party=employee_party) >>> employee.save() >>> user = User(config.user) >>> user.employees.append(employee) >>> user.employee = employee >>> user.save() >>> set_user(user.id) Create fiscal year:: >>> fiscalyear = set_fiscalyear_invoice_sequences( ... create_fiscalyear(today=today)) >>> fiscalyear.click('create_period') Get accounts:: >>> accounts = get_accounts() >>> revenue = accounts['revenue'] >>> expense = accounts['expense'] >>> cash = accounts['cash'] >>> 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 tax:: >>> tax = create_tax(Decimal('.10')) >>> tax.save() Create parties:: >>> Party = Model.get('party.party') >>> supplier = Party(name='Supplier') >>> supplier.customer_code = '1234' >>> supplier.save() >>> customer = Party(name='Customer') >>> customer.save() Create account categories:: >>> ProductCategory = Model.get('product.category') >>> account_category = ProductCategory(name="Account Category") >>> account_category.accounting = True >>> account_category.account_expense = expense >>> account_category.account_revenue = revenue >>> account_category.save() >>> account_category_tax, = account_category.duplicate() >>> account_category_tax.supplier_taxes.append(tax) >>> account_category_tax.save() Create product:: >>> ProductUom = Model.get('product.uom') >>> unit, = ProductUom.find([('name', '=', 'Unit')]) >>> ProductTemplate = Model.get('product.template') >>> template = ProductTemplate() >>> template.name = 'product' >>> template.default_uom = unit >>> template.type = 'goods' >>> template.purchasable = True >>> template.list_price = Decimal('10') >>> template.cost_price_method = 'fixed' >>> template.account_category = account_category_tax >>> template.save() >>> product, = template.products >>> template = ProductTemplate() >>> template.name = 'service' >>> template.default_uom = unit >>> template.type = 'service' >>> template.purchasable = True >>> template.list_price = Decimal('10') >>> template.cost_price_method = 'fixed' >>> template.account_category = account_category >>> template.save() >>> service, = template.products Create payment term:: >>> payment_term = create_payment_term() >>> payment_term.save() Create an Inventory:: >>> Inventory = Model.get('stock.inventory') >>> Location = Model.get('stock.location') >>> storage, = Location.find([ ... ('code', '=', 'STO'), ... ]) >>> inventory = Inventory() >>> inventory.location = storage >>> inventory_line = inventory.lines.new(product=product) >>> inventory_line.quantity = 100.0 >>> inventory_line.expected_quantity = 0.0 >>> inventory.click('confirm') >>> inventory.state 'done' Purchase 5 products:: >>> Purchase = Model.get('purchase.purchase') >>> PurchaseLine = Model.get('purchase.line') >>> purchase = Purchase() >>> purchase.party = supplier >>> purchase.payment_term = payment_term >>> purchase.invoice_method = 'order' >>> purchase_line = PurchaseLine() >>> purchase.lines.append(purchase_line) >>> purchase_line.product = product >>> purchase_line.quantity = 2.0 >>> purchase_line.unit_price = Decimal('5.0000') >>> purchase_line = PurchaseLine() >>> purchase.lines.append(purchase_line) >>> purchase_line.type = 'comment' >>> purchase_line.description = 'Comment' >>> purchase_line = PurchaseLine() >>> purchase.lines.append(purchase_line) >>> purchase_line.product = product >>> purchase_line.quantity = 3.0 >>> purchase_line.unit_price = Decimal('5.0000') >>> purchase.click('quote') >>> purchase.untaxed_amount, purchase.tax_amount, purchase.total_amount (Decimal('25.00'), Decimal('2.50'), Decimal('27.50')) >>> assertEqual(purchase.quoted_by, employee) >>> purchase.click('confirm') >>> purchase.untaxed_amount, purchase.tax_amount, purchase.total_amount (Decimal('25.00'), Decimal('2.50'), Decimal('27.50')) >>> assertEqual(purchase.confirmed_by, employee) >>> purchase.state 'processing' >>> purchase.shipment_state 'waiting' >>> purchase.invoice_state 'pending' >>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices) (2, 0, 1) >>> invoice, = purchase.invoices >>> assertEqual(invoice.origins, purchase.rec_name) >>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount (Decimal('25.00'), Decimal('2.50'), Decimal('27.50')) Invoice line must be linked to stock move:: >>> invoice_line1, invoice_line2 = sorted( ... invoice.lines, key=lambda l: l.quantity or 0) >>> stock_move1, stock_move2 = sorted(purchase.moves, ... key=lambda m: m.quantity) >>> assertEqual(invoice_line1.stock_moves, [stock_move1]) >>> assertEqual(stock_move1.invoice_lines, [invoice_line1]) >>> assertEqual(invoice_line2.stock_moves, [stock_move2]) >>> assertEqual(stock_move2.invoice_lines, [invoice_line2]) Check actual quantity:: >>> for line in purchase.lines: ... assertEqual(line.quantity, line.actual_quantity) Post invoice and check no new invoices:: >>> invoice.invoice_date = today >>> invoice.click('post') >>> purchase.reload() >>> purchase.shipment_state 'waiting' >>> purchase.invoice_state 'awaiting payment' >>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices) (2, 0, 1) Purchase 5 products with an invoice method 'on shipment':: >>> purchase = Purchase() >>> purchase.party = supplier >>> purchase.payment_term = payment_term >>> purchase.invoice_method = 'shipment' >>> purchase_line = PurchaseLine() >>> purchase.lines.append(purchase_line) >>> purchase_line.product = product >>> purchase_line.quantity = 2.0 >>> purchase_line = PurchaseLine() >>> purchase.lines.append(purchase_line) >>> purchase_line.type = 'comment' >>> purchase_line.description = 'Comment' >>> purchase_line = PurchaseLine() >>> purchase.lines.append(purchase_line) >>> purchase_line.product = product >>> purchase_line.quantity = 3.0 >>> purchase.click('quote') >>> purchase.click('confirm') >>> purchase.state 'processing' >>> purchase.shipment_state 'waiting' >>> purchase.invoice_state 'none' >>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices) (2, 0, 0) Not yet linked to invoice lines:: >>> stock_move1, stock_move2 = sorted(purchase.moves, ... key=lambda m: m.quantity) >>> len(stock_move1.invoice_lines) 0 >>> len(stock_move2.invoice_lines) 0 Validate Shipments:: >>> Move = Model.get('stock.move') >>> ShipmentIn = Model.get('stock.shipment.in') >>> shipment = ShipmentIn() >>> shipment.supplier = supplier >>> for move in purchase.moves: ... incoming_move = Move(id=move.id) ... shipment.incoming_moves.append(incoming_move) >>> shipment.save() >>> assertEqual(shipment.origins, purchase.rec_name) >>> shipment.click('receive') >>> shipment.click('do') >>> purchase.reload() >>> purchase.shipment_state 'received' >>> len(purchase.shipments), len(purchase.shipment_returns) (1, 0) Open supplier invoice:: >>> purchase.invoice_state 'pending' >>> invoice, = purchase.invoices >>> invoice.type 'in' >>> invoice_line1, invoice_line2 = sorted(invoice.lines, ... key=lambda l: l.quantity or 0) >>> for line in invoice.lines: ... line.quantity = 1 ... line.save() >>> invoice.invoice_date = today >>> invoice.click('post') Invoice lines must be linked to each stock moves:: >>> assertEqual(invoice_line1.stock_moves, [stock_move1]) >>> assertEqual(invoice_line2.stock_moves, [stock_move2]) Check second invoices:: >>> purchase.reload() >>> len(purchase.invoices) 2 >>> sum(l.quantity for i in purchase.invoices for l in i.lines) 5.0 Create the report:: >>> purchase_report = Report('purchase.purchase') >>> ext, _, _, name = purchase_report.execute([purchase], {}) >>> ext 'odt' >>> name 'Purchase-2' Create a Return:: >>> return_ = Purchase() >>> return_.party = supplier >>> return_.payment_term = payment_term >>> return_.invoice_method = 'shipment' >>> return_line = PurchaseLine() >>> return_.lines.append(return_line) >>> return_line.product = product >>> return_line.quantity = -4. >>> return_line = PurchaseLine() >>> return_.lines.append(return_line) >>> return_line.type = 'comment' >>> return_line.description = 'Comment' >>> return_.click('quote') >>> return_.click('confirm') >>> return_.state 'processing' >>> return_.shipment_state 'waiting' >>> return_.invoice_state 'none' >>> (len(return_.shipments), len(return_.shipment_returns), ... len(return_.invoices)) (0, 1, 0) Check Return Shipments:: >>> ShipmentReturn = Model.get('stock.shipment.in.return') >>> ship_return, = return_.shipment_returns >>> ship_return.state 'waiting' >>> move_return, = ship_return.moves >>> move_return.product.rec_name 'product' >>> move_return.quantity 4.0 >>> ship_return.click('assign_try') >>> ship_return.click('do') >>> ship_return.state 'done' >>> return_.reload() >>> return_.state 'processing' >>> return_.shipment_state 'received' >>> return_.invoice_state 'pending' Open supplier credit note:: >>> credit_note, = return_.invoices >>> credit_note.type 'in' >>> len(credit_note.lines) 1 >>> sum(l.quantity for l in credit_note.lines) -4.0 >>> credit_note.invoice_date = today >>> credit_note.click('post') Mixing return and purchase:: >>> mix = Purchase() >>> mix.party = supplier >>> mix.payment_term = payment_term >>> mix.invoice_method = 'order' >>> mixline = PurchaseLine() >>> mix.lines.append(mixline) >>> mixline.product = product >>> mixline.quantity = 7. >>> mixline_comment = PurchaseLine() >>> mix.lines.append(mixline_comment) >>> mixline_comment.type = 'comment' >>> mixline_comment.description = 'Comment' >>> mixline2 = PurchaseLine() >>> mix.lines.append(mixline2) >>> mixline2.product = product >>> mixline2.quantity = -2. >>> mix.click('quote') >>> mix.click('confirm') >>> mix.state 'processing' >>> mix.shipment_state 'waiting' >>> mix.invoice_state 'pending' >>> len(mix.moves), len(mix.shipment_returns), len(mix.invoices) (2, 1, 1) Checking Shipments:: >>> mix_return, = mix.shipment_returns >>> mix_shipment = ShipmentIn() >>> mix_shipment.supplier = supplier >>> for move in mix.moves: ... if move.id in [m.id for m in mix_return.moves]: ... continue ... incoming_move = Move(id=move.id) ... mix_shipment.incoming_moves.append(incoming_move) >>> mix_shipment.click('receive') >>> mix_shipment.click('do') >>> mix.reload() >>> len(mix.shipments) 1 >>> mix_return.click('wait') >>> mix_return.click('assign_try') >>> mix_return.click('do') >>> move_return, = mix_return.moves >>> move_return.product.rec_name 'product' >>> move_return.quantity 2.0 Checking the invoice:: >>> mix.reload() >>> mix_invoice, = mix.invoices >>> mix_invoice.type 'in' >>> len(mix_invoice.lines) 2 >>> sorted(l.quantity for l in mix_invoice.lines) [-2.0, 7.0] >>> mix_invoice.invoice_date = today >>> mix_invoice.click('post') Mixing stuff with an invoice method 'on shipment':: >>> mix = Purchase() >>> mix.party = supplier >>> mix.payment_term = payment_term >>> mix.invoice_method = 'shipment' >>> mixline = PurchaseLine() >>> mix.lines.append(mixline) >>> mixline.product = product >>> mixline.quantity = 6. >>> mixline_comment = PurchaseLine() >>> mix.lines.append(mixline_comment) >>> mixline_comment.type = 'comment' >>> mixline_comment.description = 'Comment' >>> mixline2 = PurchaseLine() >>> mix.lines.append(mixline2) >>> mixline2.product = product >>> mixline2.quantity = -3. >>> mix.click('quote') >>> mix.click('confirm') >>> mix.state 'processing' >>> mix.shipment_state 'waiting' >>> mix.invoice_state 'none' >>> len(mix.moves), len(mix.shipment_returns), len(mix.invoices) (2, 1, 0) Checking Shipments:: >>> mix_return, = mix.shipment_returns >>> mix_shipment = ShipmentIn() >>> mix_shipment.supplier = supplier >>> for move in mix.moves: ... if move.id in [m.id for m in mix_return.moves]: ... continue ... incoming_move = Move(id=move.id) ... mix_shipment.incoming_moves.append(incoming_move) >>> mix_shipment.click('receive') >>> mix_shipment.click('do') >>> mix.reload() >>> len(mix.shipments) 1 >>> mix_return.click('wait') >>> mix_return.click('assign_try') >>> mix_return.click('do') >>> move_return, = mix_return.moves >>> move_return.product.rec_name 'product' >>> move_return.quantity 3.0 Purchase services:: >>> service_purchase = Purchase() >>> service_purchase.party = supplier >>> service_purchase.payment_term = payment_term >>> purchase_line = service_purchase.lines.new() >>> purchase_line.product = service >>> purchase_line.quantity = 1 >>> purchase_line.unit_price = Decimal('10.0000') >>> service_purchase.save() >>> service_purchase.click('quote') >>> service_purchase.click('confirm') >>> service_purchase.state 'processing' >>> service_purchase.shipment_state 'none' >>> service_purchase.invoice_state 'pending' >>> service_invoice, = service_purchase.invoices Pay the service invoice:: >>> service_invoice.invoice_date = today >>> service_invoice.click('post') >>> pay = service_invoice.click('pay') >>> pay.form.payment_method = payment_method >>> pay.form.amount = service_invoice.total_amount >>> pay.execute('choice') >>> service_invoice.reload() >>> service_invoice.state 'paid' Check service purchase states:: >>> service_purchase.reload() >>> service_purchase.invoice_state 'paid' >>> service_purchase.shipment_state 'none' >>> service_purchase.state 'done' Create a purchase to be invoiced on shipment partially and check correctly linked to invoices:: >>> purchase = Purchase() >>> purchase.party = supplier >>> purchase.payment_term = payment_term >>> purchase.invoice_method = 'shipment' >>> line = purchase.lines.new() >>> line.product = product >>> line.quantity = 10.0 >>> purchase.click('quote') >>> purchase.click('confirm') >>> shipment = ShipmentIn() >>> shipment.supplier = supplier >>> for move in purchase.moves: ... incoming_move = Move(id=move.id) ... incoming_move.quantity = 5.0 ... shipment.incoming_moves.append(incoming_move) >>> shipment.save() >>> for move in shipment.inventory_moves: ... move.quantity = 5.0 >>> shipment.click('receive') >>> shipment.click('do') >>> purchase.reload() >>> invoice, = purchase.invoices >>> invoice_line, = invoice.lines >>> invoice_line.quantity 5.0 >>> stock_move, = invoice_line.stock_moves >>> stock_move.quantity 5.0 >>> stock_move.state 'done' Create a purchase to be invoiced on order, partially send it and check correctly linked to invoices:: >>> purchase = Purchase() >>> purchase.party = supplier >>> purchase.payment_term = payment_term >>> purchase.invoice_method = 'order' >>> line = purchase.lines.new() >>> line.product = product >>> line.quantity = 10.0 >>> purchase.click('quote') >>> purchase.click('confirm') >>> shipment = ShipmentIn() >>> shipment.supplier = supplier >>> for move in purchase.moves: ... incoming_move = Move(id=move.id) ... incoming_move.quantity = 8.0 ... shipment.incoming_moves.append(incoming_move) >>> shipment.save() >>> for move in shipment.inventory_moves: ... move.quantity = 8.0 >>> shipment.click('receive') >>> shipment.click('do') >>> purchase.reload() >>> invoice, = purchase.invoices >>> invoice_line, = invoice.lines >>> invoice_line.quantity 10.0 >>> draft_stock_move, stock_move = sorted( ... invoice_line.stock_moves, key=lambda m: m.quantity) >>> draft_stock_move.quantity 2.0 >>> draft_stock_move.state 'draft' >>> stock_move.quantity 8.0 >>> stock_move.state 'done' Deleting a line from a invoice should recreate it:: >>> purchase = Purchase() >>> purchase.party = customer >>> line = purchase.lines.new() >>> line.product = product >>> line.quantity = 10.0 >>> line.unit_price = Decimal('5.0000') >>> purchase.click('quote') >>> purchase.click('confirm') >>> invoice, = purchase.invoices >>> invoice_line, = invoice.lines >>> invoice.lines.remove(invoice_line) >>> invoice.invoice_date = today >>> invoice.click('post') >>> purchase.reload() >>> new_invoice, = purchase.invoices >>> new_invoice.number >>> len(new_invoice.lines) 1