Managage negative amounts in purchase
review354001
This commit is contained in:
1 parent
eeced05eec
commit
15d3ba7fb2
7 files changed
+626
-34
No files matched your search
@@ -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
|
||||
|
||||
@@ -24,6 +24,7 @@ def register():
|
||||
ProductSupplier,
|
||||
ProductSupplierPrice,
|
||||
ShipmentIn,
|
||||
ShipmentInReturn,
|
||||
Move,
|
||||
HandleShipmentExceptionAsk,
|
||||
HandleInvoiceExceptionAsk,
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
@@ -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
|
||||
@@ -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__':
|
||||
|
||||
Reference in new issue
Block a user