Managage negative amounts in purchase

review354001
This commit is contained in:
Cédric Krier committed 2012-09-30 21:19:45 +02:00
1 parent eeced05eec
commit 15d3ba7fb2
7 files changed
+626 -34

No files matched your search

+2
View File
@@ -1,3 +1,5 @@
* Manage negative amount in purchase
Version 2.4.0 - 2012-04-24
* Bug fixes (see mercurial logs for details)
* Add cache on amount fields in readonly states
+1
View File
@@ -24,6 +24,7 @@ def register():
ProductSupplier,
ProductSupplierPrice,
ShipmentIn,
ShipmentInReturn,
Move,
HandleShipmentExceptionAsk,
HandleInvoiceExceptionAsk,
+1
View File
@@ -65,6 +65,7 @@ of them are optional or completed with sensible default values:
- Invoices: The list of related invoices.
- Moves: The list of related stock moves.
- Shipments: The list of related shipments.
- Return Shipments: The list of the related shipment returns.
The *Purchase* report allow to print the purchase orders or to send
them by mail.
+135 -33
View File
@@ -16,9 +16,10 @@ __all__ = ['Purchase', 'PurchaseInvoice', 'PurchaseIgnoredInvoice',
'PurchaseRecreadtedInvoice', 'PurchaseLine', 'PurchaseLineTax',
'PurchaseLineInvoiceLine', 'PurchaseLineIgnoredMove',
'PurchaseLineRecreatedMove', 'PurchaseReport', 'Template', 'Product',
'ProductSupplier', 'ProductSupplierPrice', 'ShipmentIn', 'Move',
'OpenSupplier', 'HandleShipmentExceptionAsk', 'HandleShipmentException',
'HandleInvoiceExceptionAsk', 'HandleInvoiceException']
'ProductSupplier', 'ProductSupplierPrice', 'ShipmentIn',
'ShipmentInReturn', 'Move', 'OpenSupplier', 'HandleShipmentExceptionAsk',
'HandleShipmentException', 'HandleInvoiceExceptionAsk',
'HandleInvoiceException']
__metaclass__ = PoolMeta
_STATES = {
@@ -128,6 +129,9 @@ class Purchase(Workflow, ModelSQL, ModelView):
], 'Shipment State', readonly=True, required=True)
shipments = fields.Function(fields.One2Many('stock.shipment.in', None,
'Shipments'), 'get_shipments')
shipment_returns = fields.Function(
fields.One2Many('stock.shipment.in.return', None, 'Shipment Returns'),
'get_shipment_returns')
moves = fields.Function(fields.One2Many('stock.move', None, 'Moves'),
'get_moves')
@@ -452,14 +456,21 @@ class Purchase(Workflow, ModelSQL, ModelView):
'invoice_state': state,
})
def get_shipments(self, name):
shipments = []
for line in self.lines:
for move in line.moves:
if move.shipment_in:
if move.shipment_in.id not in shipments:
shipments.append(move.shipment_in.id)
return shipments
def get_shipments_returns(attribute):
"Computes the returns or shipments"
def method(self, name):
shipments = []
for line in self.lines:
for move in line.moves:
ship_or_return = getattr(move, attribute)
if bool(ship_or_return):
if ship_or_return.id not in shipments:
shipments.append(ship_or_return.id)
return shipments
return method
get_shipments = get_shipments_returns('shipment_in')
get_shipment_returns = get_shipments_returns('shipment_in_return')
def get_moves(self, name):
return [m.id for l in self.lines for m in l.moves]
@@ -562,20 +573,20 @@ class Purchase(Workflow, ModelSQL, ModelView):
'total_amount_cache': purchase.total_amount,
})
def _get_invoice_line_purchase_line(self):
def _get_invoice_line_purchase_line(self, invoice_type):
'''
Return invoice line for each purchase lines
'''
res = {}
for line in self.lines:
val = line.get_invoice_line()
val = line.get_invoice_line(invoice_type)
if val:
res[line.id] = val
return res
def _get_invoice_purchase(self):
def _get_invoice_purchase(self, invoice_type):
'''
Return invoice
Return invoice of type invoice_type
'''
pool = Pool()
Journal = pool.get('account.journal')
@@ -592,7 +603,7 @@ class Purchase(Workflow, ModelSQL, ModelView):
with Transaction().set_user(0, set_context=True):
return Invoice(
company=self.company,
type='in_invoice',
type=invoice_type,
reference=self.reference,
journal=journal,
party=self.party,
@@ -602,7 +613,7 @@ class Purchase(Workflow, ModelSQL, ModelView):
payment_term=self.payment_term,
)
def create_invoice(self):
def create_invoice(self, invoice_type):
'''
Create an invoice for the purchase and return it
'''
@@ -617,11 +628,11 @@ class Purchase(Workflow, ModelSQL, ModelView):
self.raise_user_error('missing_account_payable',
error_args=(self.party.rec_name,))
invoice_lines = self._get_invoice_line_purchase_line()
invoice_lines = self._get_invoice_line_purchase_line(invoice_type)
if not invoice_lines:
return
invoice = self._get_invoice_purchase()
invoice = self._get_invoice_purchase(invoice_type)
invoice.save()
for line in self.lines:
@@ -642,12 +653,40 @@ class Purchase(Workflow, ModelSQL, ModelView):
})
return invoice
def create_move(self):
def create_move(self, move_type):
'''
Create move for each purchase lines
'''
new_moves = []
for line in self.lines:
line.create_move()
if (line.quantity >= 0) != (move_type == 'in'):
continue
move = line.create_move()
if move:
new_moves.append(move)
return new_moves
def _get_return_shipment(self):
ShipmentInReturn = Pool().get('stock.shipment.in.return')
with Transaction().set_user(0, set_context=True):
return ShipmentInReturn(
company=self.company,
reference=self.reference,
from_location=self.warehouse.storage_location,
to_location=self.party.supplier_location,
)
def create_return_shipment(self, return_moves):
'''
Create return shipment and return the shipment id
'''
ShipmentInReturn = Pool().get('stock.shipment.in.return')
return_shipment = self._get_return_shipment()
return_shipment.moves = return_moves
return_shipment.save()
with Transaction().set_user(0, set_context=True):
ShipmentInReturn.wait([return_shipment])
return return_shipment
def is_done(self):
return ((self.invoice_state == 'paid'
@@ -707,9 +746,13 @@ class Purchase(Workflow, ModelSQL, ModelView):
for purchase in purchases:
if purchase.state in ('done', 'cancel'):
continue
purchase.create_invoice()
purchase.create_invoice('in_invoice')
purchase.create_invoice('in_credit_note')
purchase.set_invoice_state()
purchase.create_move()
purchase.create_move('in')
return_moves = purchase.create_move('return')
if return_moves:
purchase.create_return_shipment(return_moves)
purchase.set_shipment_state()
if purchase.is_done():
done.append(purchase)
@@ -1039,11 +1082,17 @@ class PurchaseLine(ModelSQL, ModelView):
return Decimal('0.0')
def get_from_location(self, name):
return self.purchase.party.supplier_location.id
if self.quantity >= 0:
return self.purchase.party.supplier_location.id
elif self.purchase.warehouse:
return self.purchase.warehouse.storage_location.id
def get_to_location(self, name):
if self.purchase.warehouse:
return self.purchase.warehouse.input_location.id
if self.quantity >= 0:
if self.purchase.warehouse:
return self.purchase.warehouse.input_location.id
else:
return self.purchase.party.supplier_location.id
def on_change_with_delivery_date(self, name=None):
if (self.product
@@ -1054,7 +1103,7 @@ class PurchaseLine(ModelSQL, ModelView):
if product_supplier.party == self.purchase.party:
return product_supplier.compute_supply_date(date=date)
def get_invoice_line(self):
def get_invoice_line(self, invoice_type):
'''
Return a list of invoice line for purchase line
'''
@@ -1070,17 +1119,22 @@ class PurchaseLine(ModelSQL, ModelView):
invoice_line.note = self.note
if self.type != 'line':
if (self.purchase.invoice_method == 'order'
and (all(l.quantity >= 0 for l in self.sale.lines
if l.type == 'line')
or all(l.quantity <= 0 for l in self.sale.lines
if l.type == 'line'))):
and ((all(l.quantity >= 0 for l in self.purchase.lines
if l.type == 'line')
and invoice_type == 'in_invoice')
or (all(l.quantity <= 0 for l in self.purchase.lines
if l.type == 'line')
and invoice_type == 'in_credit_note'))):
return [invoice_line]
else:
return []
if (invoice_type == 'in_invoice') != (self.quantity >= 0):
return []
if (self.purchase.invoice_method == 'order'
or not self.product
or self.product.type == 'service'):
quantity = self.quantity
quantity = abs(self.quantity)
else:
quantity = 0.0
for move in self.moves:
@@ -1144,7 +1198,7 @@ class PurchaseLine(ModelSQL, ModelView):
if self.product.type == 'service':
return
skip = set(self.moves_recreated)
quantity = self.quantity
quantity = abs(self.quantity)
for move in self.moves:
if move not in skip:
quantity -= Uom.compute_qty(move.uom, move.quantity,
@@ -1546,6 +1600,54 @@ class ShipmentIn:
return super(ShipmentIn, cls).draft(shipments)
class ShipmentInReturn:
__name__ = 'stock.shipment.in.return'
@classmethod
def __setup__(cls):
super(ShipmentInReturn, cls).__setup__()
cls._error_messages.update({
'reset_move': 'You cannot reset to draft a move generated '\
'by a purchase.',
})
@classmethod
def write(cls, shipments, vals):
pool = Pool()
Purchase = pool.get('purchase.purchase')
PurchaseLine = pool.get('purchase.line')
super(ShipmentInReturn, cls).write(shipments, vals)
if 'state' in vals and vals['state'] == 'done':
move_ids = []
for shipment in shipments:
move_ids.extend([x.id for x in shipment.moves])
purchase_lines = PurchaseLine.search([
('moves', 'in', move_ids),
])
purchases = set()
if purchase_lines:
for purchase_line in purchase_lines:
purchases.add(purchase_line.purchase)
with Transaction().set_user(0, set_context=True):
purchases = Purchase.browse([p.id for p in purchases])
Purchase.process(purchases)
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, shipments):
for shipment in shipments:
for move in shipment.moves:
if move.state == 'cancel' and move.purchase_line:
cls.raise_user_error('reset_move')
return super(ShipmentInReturn, cls).draft(shipments)
class Move(ModelSQL, ModelView):
__name__ = 'stock.move'
purchase_line = fields.Many2One('purchase.line', 'Purchase Line',
+22 -1
View File
@@ -130,6 +130,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="moves" colspan="4"
view_ids="purchase.move_view_list_shipment"/>
<field name="shipments" colspan="4"/>
<field name="shipment_returns" colspan="4"/>
</page>
</notebook>
<field name="currency_digits" invisible="1" colspan="6"/>
@@ -174,6 +175,17 @@ this repository contains the full copyright notices and license terms. -->
<field name="model">purchase.purchase,-1</field>
<field name="action" ref="act_shipment_form"/>
</record>
<record model="ir.action.act_window" id="act_return_form">
<field name="name">Returns</field>
<field name="res_model">stock.shipment.in.return</field>
<field name="domain">[("id", "in", Eval('shipment_returns'))]</field>
</record>
<record model="ir.action.keyword"
id="act_open_shipment_return_keyword1">
<field name="keyword">form_relate</field>
<field name="model">purchase.purchase,-1</field>
<field name="action" ref="act_return_form"/>
</record>
<record model="ir.action.act_window" id="act_invoice_form">
<field name="name">Invoices</field>
<field name="res_model">account.invoice</field>
@@ -199,7 +211,7 @@ this repository contains the full copyright notices and license terms. -->
id="choose"
yalign="0.0" xalign="0.0" xexpand="1"/>
<field name="recreate_moves" colspan="2"
view_ids="stock.move_view_tree_simple"/>
view_ids="stock.move_view_tree"/>
</form>
]]>
</field>
@@ -733,5 +745,14 @@ this repository contains the full copyright notices and license terms. -->
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_in_return_group_purchase">
<field name="model" search="[('model', '=', 'stock.shipment.in.return')]"/>
<field name="group" ref="group_purchase"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
</data>
</tryton>
+450
View File
@@ -0,0 +1,450 @@
=================
Purchase Scenario
=================
Imports::
>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> from decimal import Decimal
>>> from operator import attrgetter
>>> from proteus import config, Model, Wizard
>>> today = datetime.date.today()
Create database::
>>> config = config.set_trytond()
>>> config.pool.test = True
Install purchase::
>>> Module = Model.get('ir.module.module')
>>> purchase_module, = Module.find([('name', '=', 'purchase')])
>>> Module.install([purchase_module.id], config.context)
>>> Wizard('ir.module.module.install_upgrade').execute('upgrade')
Create company::
>>> Currency = Model.get('currency.currency')
>>> CurrencyRate = Model.get('currency.currency.rate')
>>> currencies = Currency.find([('code', '=', 'EUR')])
>>> if not currencies:
... currency = Currency(name='Euro', symbol=u'€', code='EUR',
... rounding=Decimal('0.01'), mon_grouping='[3, 3, 0]',
... mon_decimal_point=',')
... currency.save()
... CurrencyRate(date=today + relativedelta(month=1, day=1),
... rate=Decimal('1.0'), currency=currency).save()
... else:
... currency, = currencies
>>> Company = Model.get('company.company')
>>> company_config = Wizard('company.company.config')
>>> company_config.execute('company')
>>> company = company_config.form
>>> company.name = 'B2CK'
>>> company.currency = currency
>>> company_config.execute('add')
>>> company, = Company.find([])
Reload the context::
>>> User = Model.get('res.user')
>>> config._context = User.get_preferences(True, config.context)
Create fiscal year::
>>> FiscalYear = Model.get('account.fiscalyear')
>>> Sequence = Model.get('ir.sequence')
>>> SequenceStrict = Model.get('ir.sequence.strict')
>>> fiscalyear = FiscalYear(name=str(today.year))
>>> fiscalyear.start_date = today + relativedelta(month=1, day=1)
>>> fiscalyear.end_date = today + relativedelta(month=12, day=31)
>>> fiscalyear.company = company
>>> post_move_seq = Sequence(name=str(today.year), code='account.move',
... company=company)
>>> post_move_seq.save()
>>> fiscalyear.post_move_sequence = post_move_seq
>>> invoice_seq = SequenceStrict(name=str(today.year),
... code='account.invoice', company=company)
>>> invoice_seq.save()
>>> fiscalyear.out_invoice_sequence = invoice_seq
>>> fiscalyear.in_invoice_sequence = invoice_seq
>>> fiscalyear.out_credit_note_sequence = invoice_seq
>>> fiscalyear.in_credit_note_sequence = invoice_seq
>>> fiscalyear.save()
>>> FiscalYear.create_period([fiscalyear.id], config.context)
Create chart of accounts::
>>> AccountTemplate = Model.get('account.account.template')
>>> Account = Model.get('account.account')
>>> account_template, = AccountTemplate.find([('parent', '=', False)])
>>> create_chart = Wizard('account.create_chart')
>>> create_chart.execute('account')
>>> create_chart.form.account_template = account_template
>>> create_chart.form.company = company
>>> create_chart.execute('create_account')
>>> receivable, = Account.find([
... ('kind', '=', 'receivable'),
... ('company', '=', company.id),
... ])
>>> payable, = Account.find([
... ('kind', '=', 'payable'),
... ('company', '=', company.id),
... ])
>>> revenue, = Account.find([
... ('kind', '=', 'revenue'),
... ('company', '=', company.id),
... ])
>>> expense, = Account.find([
... ('kind', '=', 'expense'),
... ('company', '=', company.id),
... ])
>>> create_chart.form.account_receivable = receivable
>>> create_chart.form.account_payable = payable
>>> create_chart.execute('create_properties')
Create parties::
>>> Party = Model.get('party.party')
>>> supplier = Party(name='Supplier')
>>> supplier.save()
>>> customer = Party(name='Customer')
>>> customer.save()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> Product = Model.get('product.product')
>>> product = Product()
>>> product.name = 'product'
>>> product.default_uom = unit
>>> product.type = 'goods'
>>> product.purchasable = True
>>> product.salable = True
>>> product.list_price = Decimal('10')
>>> product.cost_price = Decimal('5')
>>> product.cost_price_method = 'fixed'
>>> product.account_expense = expense
>>> product.account_revenue = revenue
>>> product.save()
Create payment term::
>>> PaymentTerm = Model.get('account.invoice.payment_term')
>>> PaymentTermLine = Model.get('account.invoice.payment_term.line')
>>> payment_term = PaymentTerm(name='Direct')
>>> payment_term_line = PaymentTermLine(type='remainder', days=0)
>>> payment_term.lines.append(payment_term_line)
>>> 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.save()
>>> inventory_line = InventoryLine(product=product, inventory=inventory)
>>> inventory_line.quantity = 100.0
>>> inventory_line.expected_quantity = 0.0
>>> inventory.save()
>>> inventory_line.save()
>>> Inventory.confirm([inventory.id], config.context)
>>> inventory.state
u'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 = 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.save()
>>> Purchase.quote([purchase.id], config.context)
>>> Purchase.confirm([purchase.id], config.context)
>>> purchase.state
u'confirmed'
>>> purchase.reload()
>>> 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.save()
>>> Purchase.quote([purchase.id], config.context)
>>> Purchase.confirm([purchase.id], config.context)
>>> purchase.state
u'confirmed'
>>> purchase.reload()
>>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices)
(2, 0, 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()
>>> ShipmentIn.receive([shipment.id], config.context)
>>> ShipmentIn.done([shipment.id], config.context)
>>> purchase.reload()
>>> len(purchase.shipments), len(purchase.shipment_returns)
(1, 0)
Open supplier invoice::
>>> purchase.reload()
>>> Invoice = Model.get('account.invoice')
>>> invoice, = purchase.invoices
>>> invoice.type
u'in_invoice'
>>> len(invoice.lines)
2
>>> Invoice.open([invoice.id], config.context)
>>> invoice.reload()
>>> invoice.state
u'open'
>>> payable.reload()
>>> (payable.debit, payable.credit) == \
... (Decimal('0.00'), Decimal('25.00'))
True
>>> expense.reload()
>>> (expense.debit, expense.credit) == \
... (Decimal('25.00'), Decimal('0.00'))
True
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_.save()
>>> Purchase.quote([return_.id], config.context)
>>> Purchase.confirm([return_.id], config.context)
>>> return_.state
u'confirmed'
>>> return_.reload()
>>> (len(return_.shipments), len(return_.shipment_returns),
... len(return_.invoices))
(0, 1, 0)
Check Return Shipments::
>>> return_.reload()
>>> ShipmentReturn = Model.get('stock.shipment.in.return')
>>> ship_return, = return_.shipment_returns
>>> ship_return.state
u'waiting'
>>> move_return, = ship_return.moves
>>> move_return.product.name
u'product'
>>> move_return.quantity
4.0
>>> ShipmentReturn.assign_try([ship_return.id], config.context)
True
>>> ShipmentReturn.done([ship_return.id], config.context)
>>> ship_return.reload()
>>> ship_return.state
u'done'
Open supplier credit note::
>>> return_.reload()
>>> credit_note, = return_.invoices
>>> credit_note.type
u'in_credit_note'
>>> len(credit_note.lines)
1
>>> Invoice.open([credit_note.id], config.context)
>>> credit_note.reload()
>>> credit_note.state
u'open'
>>> payable.reload()
>>> (payable.debit, payable.credit) == (Decimal(20), Decimal(25))
True
>>> expense.reload()
>>> (expense.debit, expense.credit) == (Decimal(25), Decimal(20))
True
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.save()
>>> Purchase.quote([mix.id], config.context)
>>> Purchase.confirm([mix.id], config.context)
>>> mix.state
u'confirmed'
>>> mix.reload()
>>> len(mix.moves), len(mix.shipment_returns), len(mix.invoices)
(2, 1, 2)
Checking Shipments::
>>> mix.reload()
>>> mix_returns, = mix.shipment_returns
>>> mix_shipments = ShipmentIn()
>>> mix_shipments.supplier = supplier
>>> for move in mix.moves:
... if move.id in [m.id for m in mix_returns.moves]:
... continue
... incoming_move = Move(id=move.id)
... mix_shipments.incoming_moves.append(incoming_move)
>>> mix_shipments.save()
>>> ShipmentIn.receive([mix_shipments.id], config.context)
>>> ShipmentIn.done([mix_shipments.id], config.context)
>>> mix.reload()
>>> len(mix.shipments)
1
>>> ShipmentReturn.wait([mix_returns.id], config.context)
>>> ShipmentReturn.assign_try([mix_returns.id], config.context)
True
>>> ShipmentReturn.done([mix_returns.id], config.context)
>>> move_return, = mix_returns.moves
>>> move_return.product.name
u'product'
>>> move_return.quantity
2.0
Checking the invoice::
>>> mix.reload()
>>> mix_invoice, mix_credit_note = sorted(mix.invoices,
... key=attrgetter('type'), reverse=True)
>>> mix_invoice.type, mix_credit_note.type
(u'in_invoice', u'in_credit_note')
>>> len(mix_invoice.lines), len(mix_credit_note.lines)
(1, 1)
>>> Invoice.open([mix_invoice.id], config.context)
>>> mix_invoice.reload()
>>> mix_invoice.state
u'open'
>>> Invoice.open([mix_credit_note.id], config.context)
>>> mix_credit_note.reload()
>>> mix_credit_note.state
u'open'
>>> payable.reload()
>>> (payable.debit, payable.credit) == (Decimal(30), Decimal(60))
True
>>> expense.reload()
>>> (expense.debit, expense.credit) == (Decimal(60), Decimal(30))
True
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.save()
>>> Purchase.quote([mix.id], config.context)
>>> Purchase.confirm([mix.id], config.context)
>>> mix.state
u'confirmed'
>>> mix.reload()
>>> len(mix.moves), len(mix.shipment_returns), len(mix.invoices)
(2, 1, 0)
Checking Shipments::
>>> mix.reload()
>>> mix_returns, = mix.shipment_returns
>>> mix_shipments = ShipmentIn()
>>> mix_shipments.supplier = supplier
>>> for move in mix.moves:
... if move.id in [m.id for m in mix_returns.moves]:
... continue
... incoming_move = Move(id=move.id)
... mix_shipments.incoming_moves.append(incoming_move)
>>> mix_shipments.save()
>>> ShipmentIn.receive([mix_shipments.id], config.context)
>>> ShipmentIn.done([mix_shipments.id], config.context)
>>> mix.reload()
>>> len(mix.shipments)
1
>>> ShipmentReturn.wait([mix_returns.id], config.context)
>>> ShipmentReturn.assign_try([mix_returns.id], config.context)
True
>>> ShipmentReturn.done([mix_returns.id], config.context)
>>> move_return, = mix_returns.moves
>>> move_return.product.name
u'product'
>>> move_return.quantity
3.0
+15
View File
@@ -10,8 +10,10 @@ if os.path.isdir(DIR):
sys.path.insert(0, os.path.dirname(DIR))
import unittest
import doctest
import trytond.tests.test_tryton
from trytond.tests.test_tryton import test_view, test_depends
from trytond.backend.sqlite.database import Database as SQLiteDatabase
class PurchaseTestCase(unittest.TestCase):
@@ -35,10 +37,23 @@ class PurchaseTestCase(unittest.TestCase):
test_depends()
def doctest_dropdb(test):
database = SQLiteDatabase().connect()
cursor = database.cursor(autocommit=True)
try:
database.drop(cursor, ':memory:')
cursor.commit()
finally:
cursor.close()
def suite():
suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
PurchaseTestCase))
suite.addTests(doctest.DocFileSuite('scenario_purchase.rst',
setUp=doctest_dropdb, tearDown=doctest_dropdb, encoding='UTF-8',
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
return suite
if __name__ == '__main__':