[IMP] purchase: Convert yml files to python and xml

This commit is contained in:
Pragnesh Mistry
2017-11-16 14:49:05 +01:00
committed by Yannick Tivisse
parent 2d5aefb72f
commit aa13ed876e
23 changed files with 784 additions and 895 deletions
-15
View File
@@ -15,7 +15,6 @@
'security/ir.model.access.csv',
'views/account_invoice_views.xml',
'data/purchase_data.xml',
'data/purchase_data.yml',
'report/purchase_reports.xml',
'views/purchase_views.xml',
'views/stock_views.xml',
@@ -28,22 +27,8 @@
'report/purchase_order_templates.xml',
'report/purchase_quotation_templates.xml',
],
'test': [
'../account/test/account_minimal_test.xml',
'test/stock_valuation_account.xml',
'test/ui/purchase_users.yml',
'test/process/run_scheduler.yml',
'test/fifo_price.yml',
'test/fifo_returns.yml',
'test/process/cancel_order.yml',
'test/ui/duplicate_order.yml',
'test/ui/delete_order.yml',
'test/average_price.yml',
],
'demo': [
'data/purchase_order_demo.yml',
'data/purchase_demo.xml',
'data/purchase_stock_demo.yml',
],
'installable': True,
'auto_install': False,
+5
View File
@@ -36,5 +36,10 @@
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
<!-- enable purchase on main warehouse -->
<record id="stock.warehouse0" model="stock.warehouse">
<field name="buy_to_resupply" eval="True"/>
</record>
</data>
</odoo>
-5
View File
@@ -1,5 +0,0 @@
-
!python {model: stock.warehouse, id: stock.warehouse0}: |
#enable purchase on main warehouse
self.write({'buy_to_resupply': True})
+165
View File
@@ -22,5 +22,170 @@
<field name="route_ids" eval="[(4,ref('route_warehouse0_buy'))]"></field>
</record>
</data>
<record id="purchase_order_1" model="purchase.order">
<field name="partner_id" ref="base.res_partner_1"/>
<field name="state">draft</field>
<field name="order_line" model="purchase.order.line" eval="[(5, 0, 0),
(0, 0, {
'product_id': ref('product.product_delivery_01'),
'name': 'RAM SR3',
'price_unit': 79.80,
'product_qty': 15.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'product_id': ref('product.product_product_25'),
'name': 'Laptop E5023',
'price_unit': 2868.70,
'product_qty': 5.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'product_id': ref('product.product_product_27'),
'name': 'Laptop Customized',
'price_unit': 3297.20,
'product_qty': 4.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')})
]"/>
</record>
<record id="purchase_order_2" model="purchase.order">
<field name="partner_id" ref="base.res_partner_3"/>
<field name="state">draft</field>
<field name="order_line" model="purchase.order.line" eval="[(5, 0, 0),
(0, 0, {
'product_id': ref('product.product_delivery_02'),
'name': 'Multimedia Speakers',
'price_unit': 132.50,
'product_qty': 20.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'product_id': ref('product.product_delivery_01'),
'name': 'Pen drive, SP-2',
'price_unit': 89.0,
'product_qty': 5.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
]"/>
</record>
<record id="purchase_order_3" model="purchase.order">
<field name="partner_id" ref="base.res_partner_12"/>
<field name="state">draft</field>
<field name="order_line" model="purchase.order.line" eval="[(5, 0, 0),
(0, 0, {
'product_id': ref('product.product_product_2'),
'name': 'Support Services',
'price_unit': 25.50,
'product_qty': 10.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
]"/>
</record>
<record id="purchase_order_4" model="purchase.order">
<field name="partner_id" ref="base.res_partner_4"/>
<field name="state">draft</field>
<field name="order_line" model="purchase.order.line" eval="[(5, 0, 0),
(0, 0, {
'product_id': ref('product.product_delivery_02'),
'name': 'RAM SR2 (kit)',
'price_unit': 85.50,
'product_qty': 6.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'product_id': ref('product.product_product_20'),
'name': 'Motherboard I9P57',
'price_unit': 1690.0,
'product_qty': 5.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'product_id': ref('product.product_product_6'),
'name': 'iPad Mini',
'price_unit': 800.0,
'product_qty': 7.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')})
]"/>
</record>
<record id="purchase_order_5" model="purchase.order">
<field name="partner_id" ref="base.res_partner_2"/>
<field name="state">draft</field>
<field name="order_line" model="purchase.order.line" eval="[(5, 0, 0),
(0, 0, {
'product_id': ref('product.product_product_22'),
'name': 'Processor Core i5 2.70 Ghz',
'price_unit': 2010.0,
'product_qty': 3.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'product_id': ref('product.product_product_24'),
'name': 'Graphics Card',
'price_unit': 876.0,
'product_qty': 3.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
]"/>
</record>
<record id="purchase_order_6" model="purchase.order">
<field name="partner_id" ref="base.res_partner_1"/>
<field name="state">draft</field>
<field name="order_line" model="purchase.order.line" eval="[(5, 0, 0),
(0, 0, {
'product_id': ref('product.product_delivery_02'),
'name': 'Ink Cartridge',
'price_unit': 58.0,
'product_qty': 9.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'product_id': ref('product.product_delivery_01'),
'name': 'Toner Cartridge',
'price_unit': 65.0,
'product_qty': 3.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'product_id': ref('product.consu_delivery_01'),
'name': 'GrapWorks Software',
'price_unit': 154.5,
'product_qty': 4.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
]"/>
</record>
<record id="purchase_order_7" model="purchase.order">
<field name="partner_id" ref="base.res_partner_4"/>
<field name="state">draft</field>
<field name="order_line" model="purchase.order.line" eval="[(5, 0, 0),
(0, 0, {
'product_id': ref('product.product_product_12'),
'name': 'Mouse, Wireless',
'price_unit': 13.5,
'product_qty': 5.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'product_id': ref('product.product_delivery_02'),
'name': 'Webcam',
'price_unit': 38.0,
'product_qty': 15.0,
'product_uom': ref('product.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}),
]"/>
</record>
<record id="stock.stock_warehouse_shop0" model="stock.warehouse">
<field name="buy_to_resupply" eval="True"/>
</record>
</odoo>
@@ -1,141 +0,0 @@
-
Give access rights of Purchase user to create purchase order
-
!record {model: purchase.order, id: purchase_order_1}:
partner_id: base.res_partner_1
-
!record {model: purchase.order.line, id: purchase_order_line_1}:
order_id: purchase.purchase_order_1
product_id: product.product_delivery_01
name: RAM SR3
price_unit: 79.80
product_qty: 15.0
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
-
!record {model: purchase.order.line, id: purchase_order_line_2}:
order_id: purchase_order_1
product_id: product.product_product_25
name: Laptop E5023
price_unit: 2868.70
product_qty: 5.0
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
-
!record {model: purchase.order.line, id: purchase_order_line_3}:
order_id: purchase_order_1
product_id: product.product_product_27
name: Laptop Customized
price_unit: 3297.20
product_qty: 4.0
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
-
Creating second order
-
!record {model: purchase.order, id: purchase_order_2}:
partner_id: base.res_partner_3
order_line:
- product_id: product.product_delivery_02
name: Multimedia Speakers
price_unit: 132.50
product_qty: 20.0
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
- product_id: product.product_delivery_01
name: Pen drive, SP-2
price_unit: 89
product_qty: 5
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
-
!record {model: purchase.order, id: purchase_order_3}:
partner_id: base.res_partner_12
order_line:
- product_id: product.product_product_2
name: Support Services
price_unit: 25.50
product_qty: 10.0
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
-
!record {model: purchase.order, id: purchase_order_4}:
partner_id: base.res_partner_4
order_line:
- product_id: product.product_delivery_02
name: RAM SR2 (kit)
price_unit: 85.50
product_qty: 6.0
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
- product_id: product.product_product_20
name: Motherboard I9P57
price_unit: 1690
product_qty: 5.0
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
- product_id: product.product_product_6
name: iPad Mini
price_unit: 800
product_qty: 7.0
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
-
!record {model: purchase.order, id: purchase_order_5}:
partner_id: base.res_partner_2
order_line:
- product_id: product.product_product_22
name: Processor Core i5 2.70 Ghz
product_qty: 3
price_unit: 2010
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
- product_id: product.product_product_24
name: Graphics Card
product_qty: 3
price_unit: 876
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
-
!record {model: purchase.order, id: purchase_order_6}:
partner_id: base.res_partner_1
order_line:
- product_id: product.product_delivery_02
name: Ink Cartridge
price_unit: 58
product_qty: 9
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
- product_id: product.product_delivery_01
name: Toner Cartridge
product_qty: 3
price_unit: 65
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
- product_id: product.consu_delivery_01
name: GrapWorks Software
product_qty: 4
price_unit: 154.5
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
-
!record {model: purchase.order, id: purchase_order_7}:
partner_id: base.res_partner_4
order_line:
- product_id: product.product_product_12
name: Mouse, Wireless
product_qty: 5
price_unit: 13.5
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
- product_id: product.product_delivery_02
name: Webcam
product_qty: 15
price_unit: 38.0
product_uom: product.product_uom_unit
date_planned: !eval time.strftime('%Y-%m-%d')
@@ -1,8 +0,0 @@
-
!python {model: stock.warehouse, id: False}: |
pull_obj = self.env['procurement.rule']
wh_to_assign = ['stock_warehouse_shop0']
for wh_ref in wh_to_assign:
warehouse = self.browse(ref('stock.' + wh_ref))
#Force the rewriting of route and rule
warehouse.write({'buy_to_resupply': True})
-134
View File
@@ -1,134 +0,0 @@
-
Set a product as using average price.
-
!record {model: product.product, id: product_average_icecream}:
default_code: AVG
name: Average Ice Cream
type: product
categ_id: product.product_category_1
uom_id: product.product_uom_kgm
uom_po_id: product.product_uom_kgm
valuation: real_time
cost_method: average
property_stock_account_input: o_expense
property_stock_account_output: o_income
supplier_taxes_id: []
description: Average Ice Cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events.
-
I create a draft Purchase Order for first incoming shipment for 10 pieces at 60€
-
!record {model: purchase.order, id: purchase_order_average1}:
partner_id: base.res_partner_3
order_line:
- product_id: product_average_icecream
product_qty: 10.0
product_uom: product.product_uom_kgm
price_unit: 60.0
name: 'Average Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I confirm the first purchase order
-
!python {model: purchase.order, id: purchase_order_average1}: |
self.button_confirm()
-
I check the "Approved" status of purchase order 1
-
!assert {model: purchase.order, id: purchase_order_average1}:
- state == 'purchase'
-
Process the reception of purchase order 1
-
!python {model: purchase.order, id: purchase_order_average1}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
-
Check the average price of the product (average icecream).
-
!python {model: product.product, id: product_average_icecream}: |
assert self.qty_available == 10.0, 'Wrong quantity in stock after first reception'
assert self.standard_price == 60.0, 'Standard price should be the price of the first reception!'
-
I create a draft Purchase Order for second incoming shipment for 30 pieces at 80€
-
!record {model: purchase.order, id: purchase_order_average2}:
partner_id: base.res_partner_3
order_line:
- product_id: product_average_icecream
product_qty: 30.0
product_uom: product.product_uom_kgm
price_unit: 80.0
name: 'Average Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I confirm the second purchase order
-
!python {model: purchase.order, id: purchase_order_average2}: |
self.button_confirm()
-
Process the reception of purchase order 2
-
!python {model: purchase.order, id: purchase_order_average2}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
-
Check the standard price
-
!python {model: product.product, id: product_average_icecream}: |
assert self.standard_price == 75.0, 'After second reception, we should have an average price of 75.0 on the product'
-
Create picking to send some goods
-
!record {model: stock.picking, id: outgoing_average_shipment}:
picking_type_id: stock.picking_type_out
-
Create move for picking
-
!record {model: stock.move, id: outgoing_shipment_average_icecream}:
picking_id: outgoing_average_shipment
product_id: product_average_icecream
product_uom: product.product_uom_kgm
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
product_uom_qty: 20.0
-
I assign this outgoing shipment and process the delivery
-
!python {model: stock.picking, id: outgoing_average_shipment}: |
self.action_assign()
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, self.id)]}).process()
-
Check the standard price (60 * 10 + 30 * 80) / 40 = 75.0 did not change
-
!python {model: product.product, id: product_average_icecream}: |
assert self.standard_price == 75.0, 'Average price should not have changed with outgoing picking!'
assert self.qty_available == 20.0, 'Pieces were not picked correctly as the quantity on hand is wrong'
-
Make a new purchase order with 500 g Average Ice Cream at a price of 0.2€/g
-
!record {model: purchase.order, id: purchase_order_average3}:
partner_id: base.res_partner_3
order_line:
- product_id: product_average_icecream
product_qty: 0.5
product_uom: product.product_uom_kgm
price_unit: 200
name: 'Average Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I confirm the first purchase order
-
!python {model: purchase.order, id: purchase_order_average3}: |
self.button_confirm()
-
Process the reception of purchase order 3 in grams
-
!python {model: purchase.order, id: purchase_order_average3}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
-
Check price is (75.0*20 + 200*0.5) / 20.5 = 78.04878
-
!python {model: product.product, id: product_average_icecream}: |
assert self.qty_available == 20.5, 'Reception of purchase order in grams leads to wrong quantity in stock'
assert round(self.standard_price, 2) == 78.05, 'Standard price as average price of third reception with other UoM incorrect! Got %s instead of 78.05' % (round(self.standard_price, 2),)
-372
View File
@@ -1,372 +0,0 @@
-
Set the company currency as EURO for the sake of repeatibility
-
!python {model: res.company}: |
company = self.env.ref('base.main_company')
company.currency_id = self.env.ref('base.EUR')
-
Set a product as using fifo price
-
!record {model: product.product, id: product_fifo_icecream}:
default_code: FIFO
name: FIFO Ice Cream
type: product
categ_id: product.product_category_1
list_price: 100.0
standard_price: 70.0
uom_id: product.product_uom_kgm
uom_po_id: product.product_uom_kgm
cost_method: fifo
valuation: real_time
property_stock_account_input: o_expense
property_stock_account_output: o_income
supplier_taxes_id: []
description: FIFO Ice Cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events.
-
I create a draft Purchase Order for first in move for 10 kg at 50 euro
-
!record {model: purchase.order, id: purchase_order_fifo1}:
partner_id: base.res_partner_3
order_line:
- product_id: product_fifo_icecream
product_qty: 10.0
product_uom: product.product_uom_kgm
price_unit: 50.0
name: 'FIFO Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I confirm the first purchase order
-
!python {model: purchase.order, id: purchase_order_fifo1}: |
self.button_confirm()
-
I check the "Purchase" status of purchase order 1
-
!assert {model: purchase.order, id: purchase_order_fifo1}:
- state == 'purchase'
-
Process the reception of purchase order 1 and set date
-
!python {model: purchase.order, id: purchase_order_fifo1}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
-
Check the standard price of the product (fifo icecream), that should have not changed because the standard price is supposed to be updated only when goods are going out of the stock
-
!python {model: product.product, id: product_fifo_icecream}: |
assert self.standard_price == 70.0, 'Standard price should not have changed!'
assert self.stock_value == 500.0, 'Wrong stock value'
-
I create a draft Purchase Order for second shipment for 30 kg at 80 euro
-
!record {model: purchase.order, id: purchase_order_fifo2}:
partner_id: base.res_partner_3
order_line:
- product_id: product_fifo_icecream
product_qty: 30.0
product_uom: product.product_uom_kgm
price_unit: 80.0
name: 'FIFO Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I confirm the second purchase order
-
!python {model: purchase.order, id: purchase_order_fifo2}: |
self.button_confirm()
-
Process the reception of purchase order 2
-
!python {model: purchase.order, id: purchase_order_fifo2}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
-
Check the standard price of the product, that should have not changed because the standard price is supposed to be updated only when goods are going out of the stock
-
!python {model: product.product, id: product_fifo_icecream}: |
assert self.standard_price == 70.0, 'Standard price as fifo price of second reception incorrect!'
assert self.stock_value == 2900.0, 'Stock valuation should be 2900'
-
Let us send some goods
-
!record {model: stock.picking, id: outgoing_fifo_shipment}:
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
!record {model: stock.move, id: outgoing_shipment_fifo_icecream}:
picking_id: outgoing_fifo_shipment
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
product_uom_qty: 20.0
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
I assign this outgoing shipment
-
!python {model: stock.picking, id: outgoing_fifo_shipment}: |
self.action_assign()
-
Process the delivery of the outgoing shipment
-
!python {model: stock.picking, id: outgoing_fifo_shipment}: |
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, self.id)]}).process()
-
Check stock valuation is 1600
-
!python {model: product.product, id: product_fifo_icecream}: |
assert self.stock_value == 1600.0, 'Stock valuation should be 1600'
-
Do a delivery of an extra 500 g (delivery order)
-
!record {model: stock.picking, id: outgoing_fifo_shipment_uom}:
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
!record {model: stock.move, id: outgoing_shipment_fifo_icecream_gram}:
picking_id: outgoing_fifo_shipment_uom
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
product_uom_qty: 0.5
picking_type_id: stock.picking_type_out
-
I assign this outgoing shipment
-
!python {model: stock.picking, id: outgoing_fifo_shipment_uom}: |
self.action_assign()
-
Process the delivery of the outgoing shipment
-
!python {model: stock.picking, id: outgoing_fifo_shipment_uom}: |
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, self.id)]}).process()
-
Check stock valuation and qty in stock
-
!python {model: product.product, id: product_fifo_icecream}: |
assert self.stock_value == 1560.0, 'Stock valuation should be 1560'
assert self.qty_available == 19.5, 'Should still have 19.5 in stock'
-
We will temporarily change the currency rate on the sixth of June to have the same results all year
-
!record {model: res.currency, id: new_USD}:
name: new_usd
symbol: "$"
rate_ids:
- rate: 1.2834
name: !eval time.strftime('%Y-%m-%d')
-
I create a draft Purchase Order for 30000 g at 0.150 USD/g and 10 kg at 150 USD/kg
-
!record {model: purchase.order, id: purchase_order_fifo_usd}:
partner_id: base.res_partner_3
currency_id: new_USD
order_line:
- product_id: product_fifo_icecream
product_qty: 30
product_uom: product.product_uom_kgm
price_unit: 0.150
name: 'FIFO Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
- product_id: product_fifo_icecream
product_qty: 10.0
product_uom: product.product_uom_kgm
price_unit: 150.0
name: 'FIFO Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I confirm the purchase order in USD
-
!python {model: purchase.order, id: purchase_order_fifo_usd}: |
self.button_confirm()
-
Process the reception of purchase order with usd
-
!python {model: purchase.order, id: purchase_order_fifo_usd}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
-
We create delivery order of 49.5 kg
-
!record {model: stock.picking, id: outgoing_fifo_shipment_cur}:
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
!record {model: stock.move, id: outgoing_shipment_fifo_icecream_cur}:
picking_id: outgoing_fifo_shipment_cur
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
product_uom_qty: 49.5
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
I assign this outgoing shipment
-
!python {model: stock.picking, id: outgoing_fifo_shipment_cur}: |
self.action_assign()
-
Process the delivery of the outgoing shipment
-
!python {model: stock.picking, id: outgoing_fifo_shipment_cur}: |
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, self.id)]}).process()
-
Do a delivery of an extra 10 kg
-
!record {model: stock.picking, id: outgoing_fifo_shipment_ret}:
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
!record {model: stock.move, id: outgoing_shipment_fifo_icecream_ret}:
picking_id: outgoing_fifo_shipment_ret
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
product_uom_qty: 10.0
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
I assign this outgoing shipment
-
!python {model: stock.picking, id: outgoing_fifo_shipment_ret}: |
self.action_assign()
-
Process the delivery of the outgoing shipment
-
!python {model: stock.picking, id: outgoing_fifo_shipment_ret}: |
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, self.id)]}).process()
-
Check rounded price is 150.0 / 1.2834
-
!python {model: product.product, id: product_fifo_icecream}: |
assert self.qty_available == 0.0, 'Wrong quantity in stock after first reception'
-
Let us create some outs to get negative stock for a new product using the same config
-
!record {model: product.product, id: product_fifo_negative}:
default_code: NEG
name: FIFO Negative
type: product
categ_id: product.product_category_1
list_price: 100.0
standard_price: 70.0
uom_id: product.product_uom_kgm
uom_po_id: product.product_uom_kgm
cost_method: fifo
valuation: real_time
property_stock_account_input: o_expense
property_stock_account_output: o_income
supplier_taxes_id: []
description:
-
Create outpicking. We create delivery order of 100 kg.
-
!record {model: stock.picking, id: outgoing_fifo_shipment_neg}:
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
!record {model: stock.move, id: outgoing_shipment_fifo_icecream_neg}:
picking_id: outgoing_fifo_shipment_neg
product_id: product_fifo_negative
product_uom: product.product_uom_kgm
product_uom_qty: 100.0
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
Process the delivery of the first outgoing shipment
-
!python {model: stock.picking, id: outgoing_fifo_shipment_neg}: |
self.action_confirm()
move = self.move_lines[0]
move.quantity_done = 100
self.action_done()
assert move.product_id.qty_available == -100, 'Stock qty should be -100'
-
The behavior of fifo/lifo is not guaranteed if the quants are created at the same second, so I just wait one second
-
!python {model: stock.picking, id: False}: |
import time
time.sleep(1)
-
Let us create another out of 400 kg
-
!record {model: stock.picking, id: outgoing_fifo_shipment_neg2}:
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
!record {model: stock.move, id: outgoing_shipment_fifo_icecream_neg2}:
picking_id: outgoing_fifo_shipment_neg2
product_id: product_fifo_negative
product_uom: product.product_uom_kgm
product_uom_qty: 400.0
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
Process the delivery of the outgoing shipments
-
!python {model: stock.picking, id: outgoing_fifo_shipment_neg2}: |
self.action_confirm()
move = self.move_lines[0]
move.quantity_done = 400
self.action_done()
assert move.product_id.qty_available == -500, 'Stock qty should be -500'
-
Receive purchase order with 50 kg FIFO Ice Cream at 50 euro/kg
-
!record {model: purchase.order, id: purchase_order_fifo_neg}:
partner_id: base.res_partner_3
order_line:
- product_id: product_fifo_negative
product_qty: 50.0
product_uom: product.product_uom_kgm
price_unit: 50.0
name: 'FIFO Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I confirm the first purchase order
-
!python {model: purchase.order, id: purchase_order_fifo_neg}: |
self.button_confirm()
-
Process the reception of purchase order 1
-
!python {model: purchase.order, id: purchase_order_fifo_neg}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
-
Receive purchase order with 600 kg FIFO Ice Cream at 80 euro/kg
-
!record {model: purchase.order, id: purchase_order_fifo_neg2}:
partner_id: base.res_partner_3
order_line:
- product_id: product_fifo_negative
product_qty: 600.0
product_uom: product.product_uom_kgm
price_unit: 80
name: 'FIFO Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I confirm the second negative purchase order
-
!python {model: purchase.order, id: purchase_order_fifo_neg2}: |
self.button_confirm()
-
Process the reception of purchase order 2
-
!python {model: purchase.order, id: purchase_order_fifo_neg2}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
original_out_move = self.env['stock.picking'].browse(ref('outgoing_fifo_shipment_neg')).move_lines[0]
outgoing_shipment_fifo_icecream_neg2 = self.env['stock.picking'].browse(ref('outgoing_fifo_shipment_neg2')).move_lines[0]
original_out_move._fifo_vacuum()
outgoing_shipment_fifo_icecream_neg2._fifo_vacuum()
assert original_out_move.product_id.stock_value == 12000.0, 'Stock Value should be 12000'
assert original_out_move.product_id.qty_available == 150.0, 'Qty available should be 150'
-86
View File
@@ -1,86 +0,0 @@
-
Set a product as using fifo price
-
!record {model: product.product, id: product_fiforet_icecream}:
default_code: FIFORET
name: FIFO Ice Cream
type: product
standard_price: 0.0
categ_id: product.product_category_1
uom_id: product.product_uom_kgm
uom_po_id: product.product_uom_kgm
cost_method: fifo
valuation: real_time
property_stock_account_input: o_expense
property_stock_account_output: o_income
supplier_taxes_id: []
description: FIFO Ice Cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events.
-
I create a draft Purchase Order for first in move for 10 kg at 50 euro
-
!record {model: purchase.order, id: purchase_order_fiforet1}:
partner_id: base.res_partner_3
order_line:
- product_id: product_fiforet_icecream
product_qty: 10.0
product_uom: product.product_uom_kgm
price_unit: 50.0
name: 'FIFO Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I create a draft Purchase Order for second shipment for 30 kg at 80 euro
-
!record {model: purchase.order, id: purchase_order_fiforet2}:
partner_id: base.res_partner_3
order_line:
- product_id: product_fiforet_icecream
product_qty: 30.0
product_uom: product.product_uom_kgm
price_unit: 80.0
name: 'FIFO Ice Cream'
date_planned: !eval time.strftime('%Y-%m-%d')
-
I confirm the first purchase order
-
!python {model: purchase.order, id: purchase_order_fiforet1}: |
self.button_confirm()
-
Process the reception of purchase order 1
-
!python {model: purchase.order, id: purchase_order_fiforet1}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
-
Check the standard price of the product (fifo icecream)
-
!python {model: product.product, id: product_fiforet_icecream}: |
assert self.standard_price == 0.0, 'Standard price should not have changed!'
-
I confirm the second purchase order
-
!python {model: purchase.order, id: purchase_order_fiforet2}: |
self.button_confirm()
-
Process the reception of purchase order 2
-
!python {model: purchase.order, id: purchase_order_fiforet2}: |
picking = self.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
-
Return the goods of purchase order 2
-
!python {model: purchase.order, id: purchase_order_fiforet2}: |
picking = self.picking_ids[0]
return_id = self.env['stock.return.picking'].with_context(active_model='stock.picking', active_id=picking.id).create({})
return_picking_id, dummy = return_id.with_context(active_id=picking.id)._create_returns()
# Important to pass through confirmation and assignation
return_picking = self.env['stock.picking'].browse(return_picking_id)
return_picking.action_confirm()
return_picking.move_lines[0].quantity_done = return_picking.move_lines[0].product_uom_qty
return_picking.action_done()
-
After the return only 10 of the second purchase order should still be in stock as it applies fifo on the return too
-
!python {model: product.product, id: product_fiforet_icecream}: |
assert self.qty_available == 10.0, 'Qty available should be 10.0'
assert self.stock_value == 800, 'Stock value should be 800'
@@ -1,32 +0,0 @@
-
Purchase user can also cancel order therfore test with that user which have Purchase user rights.
-
!context
uid: 'res_users_purchase_user'
-
In order to test the cancel flow, I start it from canceling confirmed purchase order.
-
I confirm the purchase order.
-
!python {model: purchase.order, id: purchase_order_5}: |
self.button_confirm()
-
I check the "Approved" status after confirmed RFQ.
-
!assert {model: purchase.order, id: purchase_order_5}:
- state == 'purchase'
-
First I cancel receptions related to this order if order shipped.
-
!python {model: purchase.order, id: purchase_order_5}: |
self.env['stock.picking'].browse([picking.id for picking in self.picking_ids]).action_cancel()
-
Now I am able to cancel purchase order.
-
!python {model: purchase.order, id: purchase_order_5}: |
self.button_cancel()
-
I check that order is cancelled.
-
!assert {model: purchase.order, id: purchase_order_5}:
- state == 'cancel'
@@ -1,15 +0,0 @@
-
In order to test the scheduler to generate RFQ, I create a new product
-
!record {model: product.product, id: scheduler_product, view: False}:
name: scheduler prod
type: product
seller_ids:
- delay: 1
name: base.res_partner_2
min_qty: 5.0
-
Add Buy route
-
!python {model: product.product, id: scheduler_product}: |
self.write({"route_ids": [(4, ref("purchase.route_warehouse0_buy"))]})
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record forcecreate="True" id="property_stock_valuation_account_id" model="ir.property">
<field name="name">property_stock_valuation_account_id</field>
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_valuation_account_id')]"/>
<field eval="'account.account,'+str(ref('stk'))" model="account.account" name="value"/>
<field name="company_id" ref="base.main_company"/>
</record>
<record forcecreate="True" id="property_stock_journal" model="ir.property">
<field name="name">property_stock_journal</field>
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_journal'),('relation', '=', 'account.journal')]"/>
<field eval="'account.journal,'+str(ref('miscellaneous_journal'))" model="account.journal" name="value"/>
<field name="company_id" ref="base.main_company"/>
</record>
</data>
</odoo>
-31
View File
@@ -1,31 +0,0 @@
-
Give access rights of Purchase user to delete purchase order
-
!context
uid: 'res_users_purchase_user'
-
In order to test to delete process on purchase order.
-
I try to delete confirmed order and check Error Message.
-
!python {model: purchase.order, id: purchase_order_1}: |
try:
self.unlink()
except Exception:
pass
-
I delete a cancelled order.
-
!python {model: purchase.order, id: purchase_order_7}: |
from odoo.exceptions import UserError
try:
self.unlink()
except UserError:
pass
-
I delete a draft order.
-
!python {model: purchase.order, id: purchase_order_5}: |
self.unlink()
@@ -1,7 +0,0 @@
-
In order to test the duplicate order and check duplicate details.
-
I duplicate order.
-
!python {model: purchase.order, id: purchase_order_1}: |
self.copy()
@@ -1,28 +0,0 @@
-
Create a user as 'Purchase manager'
-
!record {model: res.users, id: res_users_purchase_manager, view: False}:
company_id: base.main_company
name: Purchase Manager
login: pm
email: purchasemanager@yourcompany.com
-
I added groups for Purchase manager.
-
!record {model: res.users, id: res_users_purchase_manager}:
groups_id:
- purchase.group_purchase_manager
-
Create a user as 'Purchase user'
-
!record {model: res.users, id: res_users_purchase_user, view: False}:
company_id: base.main_company
name: Purchase User
login: pu
email: purchaseuser@yourcompany.com
-
I added groups for Purchase user.
-
!record {model: res.users, id: res_users_purchase_user}:
groups_id:
- purchase.group_purchase_user
+7 -2
View File
@@ -1,8 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_onchange_product_id
from . import test_purchase_order
from . import test_average_price
from . import test_create_picking
from . import test_fifo_price
from . import test_fifo_returns
from . import test_onchange_product
from . import test_purchase_delete_order
from . import test_purchase_lead_time
from . import test_purchase_order
from . import test_purchase_order_process
from . import test_stockvaluation
+15
View File
@@ -3,6 +3,8 @@ from datetime import timedelta
from odoo import fields
from odoo.addons.stock.tests.common2 import TestStockCommon
from odoo import tools
from odoo.modules.module import get_module_resource
class TestPurchase(TestStockCommon):
@@ -16,6 +18,11 @@ class TestPurchase(TestStockCommon):
}
return ProcurementGroup.run(product, product_qty, self.uom_unit, self.warehouse_1.lot_stock_id, product.name, '/', order_values)
def _load(self, module, *args):
tools.convert_file(self.cr, 'purchase',
get_module_resource(module, *args),
{}, 'init', False, 'test', self.registry._assertion_report)
@classmethod
def setUpClass(cls):
super(TestPurchase, cls).setUpClass()
@@ -34,3 +41,11 @@ class TestPurchase(TestStockCommon):
'type': 'product',
'route_ids': [(6, 0, [cls.route_buy, cls.route_mto])],
'seller_ids': [(0, 0, {'name': cls.partner_1.id, 'delay': 2})]})
cls.res_users_purchase_user = cls.env['res.users'].create({
'company_id': cls.env.ref('base.main_company').id,
'name': "Purchase User",
'login': "pu",
'email': "purchaseuser@yourcompany.com",
'groups_id': [(6, 0, [cls.env.ref('purchase.group_purchase_user').id])],
})
+128
View File
@@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from .common import TestPurchase
class TestAveragePrice(TestPurchase):
def test_00_average_price(self):
""" Testcase for average price computation"""
self._load('account', 'test', 'account_minimal_test.xml')
self._load('stock_account', 'test', 'stock_valuation_account.xml')
# Set a product as using average price.
product_icecream = self.env['product.product'].create({
'default_code': 'AVG',
'name': 'Average Ice Cream',
'type': 'product',
'categ_id': self.env.ref('product.product_category_1').id,
'list_price': 100.0,
'standard_price': 60.0,
'uom_id': self.env.ref('product.product_uom_kgm').id,
'uom_po_id': self.env.ref('product.product_uom_kgm').id,
'cost_method': 'average',
'valuation': 'real_time',
'property_stock_account_input': self.ref('purchase.o_expense'),
'property_stock_account_output': self.ref('purchase.o_income'),
'supplier_taxes_id': [],
'description': 'FIFO Ice Cream',
})
# I create a draft Purchase Order for first incoming shipment for 10 pieces at 60€
purchase_order_1 = self.env['purchase.order'].create({
'partner_id': self.env.ref('base.res_partner_3').id,
'order_line': [(0, 0, {
'name': 'Average Ice Cream',
'product_id': product_icecream.id,
'product_qty': 10.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 60.0,
'date_planned': time.strftime('%Y-%m-%d'),
})]
})
# Confirm the first purchase order
purchase_order_1.button_confirm()
# Check the "Approved" status of purchase order 1
self.assertEqual(purchase_order_1.state, 'purchase', "Wrong state of purchase order!")
# Process the reception of purchase order 1
picking = purchase_order_1.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
# Check the average_price of the product (average icecream).
self.assertEqual(product_icecream.qty_available, 10.0, 'Wrong quantity in stock after first reception')
self.assertEqual(product_icecream.standard_price, 60.0, 'Standard price should be the price of the first reception!')
# I create a draft Purchase Order for second incoming shipment for 30 pieces at 80€
purchase_order_2 = self.env['purchase.order'].create({
'partner_id': self.env.ref('base.res_partner_3').id,
'order_line': [(0, 0, {
'name': product_icecream.name,
'product_id': product_icecream.id,
'product_qty': 30.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 80.0,
'date_planned': time.strftime('%Y-%m-%d'),
})]
})
# Confirm the second purchase order
purchase_order_2.button_confirm()
# Process the reception of purchase order 2
picking = purchase_order_2.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
# Check the standard price
self.assertEqual(product_icecream.standard_price, 75.0, 'After second reception, we should have an average price of 75.0 on the product')
# Create picking to send some goods
outgoing_shipment = self.env['stock.picking'].create({
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'move_lines': [(0, 0, {
'name': 'outgoing_shipment_avg_move',
'product_id': product_icecream.id,
'product_uom_qty': 20.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id})]
})
# Assign this outgoing shipment and process the delivery
outgoing_shipment.action_assign()
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, outgoing_shipment.id)]}).process()
# Check the average price (60 * 10 + 30 * 80) / 40 = 75.0€ did not change
self.assertEqual(product_icecream.standard_price, 75.0, 'Average price should not have changed with outgoing picking!')
self.assertEqual(product_icecream.qty_available, 20.0, 'Pieces were not picked correctly as the quantity on hand is wrong')
# Make a new purchase order with 500 g Average Ice Cream at a price of 0.2€/g
purchase_order_3 = self.env['purchase.order'].create({
'partner_id': self.env.ref('base.res_partner_3').id,
'order_line': [(0, 0, {
'name': product_icecream.name,
'product_id': product_icecream.id,
'product_qty': 500.0,
'product_uom': self.ref('product.product_uom_gram'),
'price_unit': 0.2,
'date_planned': time.strftime('%Y-%m-%d'),
})]
})
# Confirm the first purchase order
purchase_order_3.button_confirm()
# Process the reception of purchase order 3 in grams
picking = purchase_order_3.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
# Check price is (75.0 * 20 + 200*0.5) / 20.5 = 78.04878€
self.assertEqual(product_icecream.qty_available, 20.5, 'Reception of purchase order in grams leads to wrong quantity in stock')
self.assertEqual(round(product_icecream.standard_price, 2), 78.05,
'Standard price as average price of third reception with other UoM incorrect! Got %s instead of 78.05' % (round(product_icecream.standard_price, 2)))
+323
View File
@@ -0,0 +1,323 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from .common import TestPurchase
class TestFifoPrice(TestPurchase):
def test_00_test_fifo(self):
""" Test product cost price with fifo removal strategy."""
self._load('account', 'test', 'account_minimal_test.xml')
self._load('stock_account', 'test', 'stock_valuation_account.xml')
# Set the company currency as EURO for the sake of repeatibility
self.env.ref('base.main_company').currency_id = self.env.ref('base.EUR')
# Set a product as using fifo price
product_icecream = self.env['product.product'].create({
'default_code': 'FIFO',
'name': 'FIFO Ice Cream',
'type': 'product',
'categ_id': self.env.ref('product.product_category_1').id,
'list_price': 100.0,
'standard_price': 70.0,
'uom_id': self.env.ref('product.product_uom_kgm').id,
'uom_po_id': self.env.ref('product.product_uom_kgm').id,
'cost_method': 'fifo',
'valuation': 'real_time',
'property_stock_account_input': self.ref('purchase.o_expense'),
'property_stock_account_output': self.ref('purchase.o_income'),
'supplier_taxes_id': '[]',
'description': 'FIFO Ice Cream',
})
# I create a draft Purchase Order for first in move for 10 kg at 50 euro
purchase_order_1 = self.env['purchase.order'].create({
'partner_id': self.env.ref('base.res_partner_3').id,
'order_line': [(0, 0, {
'name': 'FIFO Ice Cream',
'product_id': product_icecream.id,
'product_qty': 10.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 50.0,
'date_planned': time.strftime('%Y-%m-%d')})],
})
# Confirm the first purchase order
purchase_order_1.button_confirm()
# Check the "Purchase" status of purchase order 1
self.assertEquals(purchase_order_1.state, 'purchase')
# Process the reception of purchase order 1 and set date
picking = purchase_order_1.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
# Check the standard price of the product (fifo icecream), that should have not changed
# because the standard price is supposed to be updated only when goods are going out of the stock
self.assertEquals(product_icecream.standard_price, 70.0, 'Standard price should not have changed')
self.assertEquals(product_icecream.stock_value, 500.0, 'Wrong stock value')
# I create a draft Purchase Order for second shipment for 30 kg at 80 euro
purchase_order_2 = self.env['purchase.order'].create({
'partner_id': self.env.ref('base.res_partner_3').id,
'order_line': [(0, 0, {
'name': 'FIFO Ice Cream',
'product_id': product_icecream.id,
'product_qty': 30.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 80.0,
'date_planned': time.strftime('%Y-%m-%d')})],
})
# Confirm the second purchase order
purchase_order_2.button_confirm()
# Process the reception of purchase order 2
picking = purchase_order_2.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
# Check the standard price of the product, that should have not changed because the
# standard price is supposed to be updated only when goods are going out of the stock
self.assertEquals(product_icecream.standard_price, 70.0, 'Standard price as fifo price of second reception incorrect!')
self.assertEquals(product_icecream.stock_value, 2900.0, 'Stock valuation should be 2900')
# Let us send some goods
outgoing_shipment = self.env['stock.picking'].create({
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'move_lines': [(0, 0, {
'name': product_icecream.name,
'product_id': product_icecream.id,
'product_uom_qty': 20.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'picking_type_id': self.env.ref('stock.picking_type_out').id})]
})
# I assign this outgoing shipment
outgoing_shipment.action_assign()
# Process the delivery of the outgoing shipment
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, outgoing_shipment.id)]}).process()
# Check stock value became 1600 .
self.assertEqual(product_icecream.stock_value, 1600.0, 'Stock valuation should be 1600')
# Do a delivery of an extra 500 g (delivery order)
outgoing_shipment_uom = self.env['stock.picking'].create({
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'move_lines': [(0, 0, {
'name': product_icecream.name,
'product_id': product_icecream.id,
'product_uom_qty': 500.0,
'product_uom': self.env.ref('product.product_uom_gram').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'picking_type_id': self.env.ref('stock.picking_type_out').id})]
})
# I assign this outgoing shipment
outgoing_shipment_uom.action_assign()
# Process the delivery of the outgoing shipment
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, outgoing_shipment_uom.id)]}).process()
# Check stock valuation and qty in stock
self.assertEqual(product_icecream.stock_value, 1560.0, 'Stock valuation should be 1560')
self.assertEqual(product_icecream.qty_available, 19.5, 'Should still have 19.5 in stock')
# We will temporarily change the currency rate on the sixth of June to have the same results all year
NewUSD = self.env['res.currency'].create({
'name': 'new_usd',
'symbol': '$²',
'rate_ids': [(0, 0, {'rate': 1.2834, 'name': time.strftime('%Y-%m-%d')})],
})
# Create PO for 30000 g at 0.150$/g and 10 kg at 150$/kg
purchase_order_usd = self.env['purchase.order'].create({
'partner_id': self.env.ref('base.res_partner_3').id,
'currency_id': NewUSD.id,
'order_line': [(0, 0, {
'name': 'FIFO Ice Cream',
'product_id': product_icecream.id,
'product_qty': 30,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 0.150,
'date_planned': time.strftime('%Y-%m-%d')}),
(0, 0, {
'name': product_icecream.name,
'product_id': product_icecream.id,
'product_qty': 10.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 150.0,
'date_planned': time.strftime('%Y-%m-%d')})]
})
# Confirm the purchase order in USD
purchase_order_usd.button_confirm()
# Process the reception of purchase order with USD
picking = purchase_order_usd.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
# Create delivery order of 49.5 kg
outgoing_shipment_cur = self.env['stock.picking'].create({
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'move_lines': [(0, 0, {
'name': product_icecream.name,
'product_id': product_icecream.id,
'product_uom_qty': 49.5,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'picking_type_id': self.env.ref('stock.picking_type_out').id})]
})
# I assign this outgoing shipment
outgoing_shipment_cur.action_assign()
# Process the delivery of the outgoing shipment
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, outgoing_shipment_cur.id)]}).process()
# Do a delivery of an extra 10 kg
outgoing_shipment_ret = self.env['stock.picking'].create({
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'move_lines': [(0, 0, {
'name': product_icecream.name,
'product_id': product_icecream.id,
'product_uom_qty': 10,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'picking_type_id': self.env.ref('stock.picking_type_out').id})]
})
# I assign this outgoing shipment
outgoing_shipment_ret.action_assign()
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, outgoing_shipment_ret.id)]}).process()
# Check rounded price is 150.0 / 1.2834
self.assertEqual(round(product_icecream.qty_available), 0.0, 'Wrong quantity in stock after first reception.')
# Let us create some outs to get negative stock for a new product using the same config
product_fifo_negative = self.env['product.product'].create({
'default_code': 'NEG',
'name': 'FIFO Negative',
'type': 'product',
'categ_id': self.env.ref('product.product_category_1').id,
'list_price': 100.0,
'standard_price': 70.0,
'uom_id': self.env.ref('product.product_uom_kgm').id,
'uom_po_id': self.env.ref('product.product_uom_kgm').id,
'cost_method': 'fifo',
'valuation': 'real_time',
'property_stock_account_input': self.ref('purchase.o_expense'),
'property_stock_account_output': self.ref('purchase.o_income'),
'supplier_taxes_id': '[]',
'description': 'FIFO Ice Cream',
})
# Create outpicking.create delivery order of 100 kg.
outgoing_shipment_neg = self.env['stock.picking'].create({
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'move_lines': [(0, 0, {
'name': product_fifo_negative.name,
'product_id': product_fifo_negative.id,
'product_uom_qty': 100,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'picking_type_id': self.env.ref('stock.picking_type_out').id})]
})
# Process the delivery of the first outgoing shipment
outgoing_shipment_neg.action_confirm()
outgoing_shipment_neg.move_lines[0].quantity_done = 100.0
outgoing_shipment_neg.action_done()
# Check qty available = -100
self.assertEqual(product_fifo_negative.qty_available, -100, 'Stock qty should be -100')
# The behavior of fifo/lifo is not garantee if the quants are created at the same second, so just wait one second
time.sleep(1)
# Let create another out shipment of 400 kg
outgoing_shipment_neg2 = self.env['stock.picking'].create({
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'move_lines': [(0, 0, {
'name': product_fifo_negative.name,
'product_id': product_fifo_negative.id,
'product_uom_qty': 400,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'location_id': self.env.ref('stock.stock_location_stock').id,
'location_dest_id': self.env.ref('stock.stock_location_customers').id,
'picking_type_id': self.env.ref('stock.picking_type_out').id})]
})
# Process the delivery of the outgoing shipments
outgoing_shipment_neg2.action_confirm()
outgoing_shipment_neg2.move_lines[0].quantity_done = 400.0
outgoing_shipment_neg2.action_done()
# Check qty available = -500
self.assertEqual(product_fifo_negative.qty_available, -500, 'Stock qty should be -500')
# Receive purchase order with 50 kg Ice Cream at 50€/kg
purchase_order_neg = self.env['purchase.order'].create({
'partner_id': self.env.ref('base.res_partner_3').id,
'order_line': [(0, 0, {
'name': 'FIFO Ice Cream',
'product_id': product_fifo_negative.id,
'product_qty': 50.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 50.0,
'date_planned': time.strftime('%Y-%m-%d')})],
})
# I confirm the first purchase order
purchase_order_neg.button_confirm()
# Process the reception of purchase order neg
picking = purchase_order_neg.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
# Receive purchase order with 600 kg FIFO Ice Cream at 80 euro/kg
purchase_order_neg2 = self.env['purchase.order'].create({
'partner_id': self.env.ref('base.res_partner_3').id,
'order_line': [(0, 0, {
'name': product_icecream.name,
'product_id': product_fifo_negative.id,
'product_qty': 600.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 80.0,
'date_planned': time.strftime('%Y-%m-%d')})],
})
# I confirm the second negative purchase order
purchase_order_neg2.button_confirm()
# Process the reception of purchase order neg2
picking = purchase_order_neg2.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
original_out_move = outgoing_shipment_neg.move_lines[0]
outgoing_shipment_fifo_icecream_neg2 = outgoing_shipment_neg2.move_lines[0]
original_out_move._fifo_vacuum()
outgoing_shipment_fifo_icecream_neg2._fifo_vacuum()
self.assertEquals(original_out_move.product_id.stock_value, 12000.0, 'Value of the move should be 2500')
self.assertEquals(original_out_move.product_id.qty_available, 150.0, 'Qty available should be 150')
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
import time
from .common import TestPurchase
class TestFifoReturns(TestPurchase):
def test_fifo_returns(self):
"""Test to create product and purchase order to test the FIFO returns of the product"""
self._load('account', 'test', 'account_minimal_test.xml')
self._load('stock_account', 'test', 'stock_valuation_account.xml')
# Set a product as using fifo price
product_fiforet_icecream = self.env['product.product'].create({
'default_code': 'FIFORET',
'name': 'FIFO Ice Cream',
'type': 'product',
'categ_id': self.env.ref('product.product_category_1').id,
'standard_price': 0.0,
'uom_id': self.env.ref('product.product_uom_kgm').id,
'uom_po_id': self.env.ref('product.product_uom_kgm').id,
'cost_method': 'fifo',
'valuation': 'real_time',
'property_stock_account_input': self.ref('purchase.o_expense'),
'property_stock_account_output': self.ref('purchase.o_income'),
'description': 'FIFO Ice Cream',
})
# I create a draft Purchase Order for first in move for 10 kg at 50 euro
purchase_order_1 = self.env['purchase.order'].create({
'partner_id': self.ref('base.res_partner_3'),
'order_line': [(0, 0, {
'name': 'FIFO Ice Cream',
'product_id': product_fiforet_icecream.id,
'product_qty': 10.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 50.0,
'date_planned': time.strftime('%Y-%m-%d'),
})],
})
# Create a draft Purchase Order for second shipment for 30kg at 80€/kg
purchase_order_2 = self.env['purchase.order'].create({
'partner_id': self.ref('base.res_partner_3'),
'order_line': [(0, 0, {
'name': 'FIFO Ice Cream',
'product_id': product_fiforet_icecream.id,
'product_qty': 30.0,
'product_uom': self.env.ref('product.product_uom_kgm').id,
'price_unit': 80.0,
'date_planned': time.strftime('%Y-%m-%d'),
})],
})
# Confirm the first purchase order
purchase_order_1.button_confirm()
# Process the reception of purchase order 1
picking = purchase_order_1.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
# Check the standard price of the product (fifo icecream)
self.assertEqual(product_fiforet_icecream.standard_price, 0.0, 'Standard price should not have changed!')
# Confirm the second purchase order
purchase_order_2.button_confirm()
picking = purchase_order_2.picking_ids[0]
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
# Return the goods of purchase order 2
picking = purchase_order_2.picking_ids[0]
return_pick_wiz = self.env['stock.return.picking'].with_context(
active_model='stock.picking', active_id=picking.id).create({})
return_picking_id, dummy = return_pick_wiz.with_context(active_id=picking.id)._create_returns()
# Important to pass through confirmation and assignation
return_picking = self.env['stock.picking'].browse(return_picking_id)
return_picking.action_confirm()
return_picking.move_lines[0].quantity_done = return_picking.move_lines[0].product_uom_qty
return_picking.action_done()
# After the return only 10 of the second purchase order should still be in stock as it applies fifo on the return too
self.assertEqual(product_fiforet_icecream.qty_available, 10.0, 'Qty available should be 10.0')
self.assertEqual(product_fiforet_icecream.stock_value, 800.0, 'Stock value should be 800')
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.exceptions import UserError
from .common import TestPurchase
class TestDeleteOrder(TestPurchase):
def test_00_delete_order(self):
''' Testcase for deleting purchase order with purchase user group'''
# In order to test delete process on purchase order,tried to delete a confirmed order and check Error Message.
purchase_order_1 = self.env.ref('purchase.purchase_order_1').sudo(self.res_users_purchase_user.id)
with self.assertRaises(UserError):
purchase_order_1.unlink()
# Delete 'cancelled' purchase order with user group
purchase_order_7 = self.env.ref('purchase.purchase_order_7').sudo(self.res_users_purchase_user.id)
purchase_order_7.button_cancel()
self.assertEqual(purchase_order_7.state, 'cancel', 'PO is cancelled!')
purchase_order_7.unlink()
# Delete 'draft' purchase order with user group
purchase_order_5 = self.env.ref('purchase.purchase_order_5').sudo(self.res_users_purchase_user.id)
self.assertEqual(purchase_order_5.state, 'draft', 'PO in draft state!')
purchase_order_5.button_cancel()
self.assertEqual(purchase_order_5.state, 'cancel', 'PO is cancelled!')
purchase_order_5.unlink()
@@ -0,0 +1,25 @@
from .common import TestPurchase
class TestPurchaseOrderProcess(TestPurchase):
def test_00_cancel_purchase_order_flow(self):
""" Test cancel purchase order with group user."""
# In order to test the cancel flow,start it from canceling confirmed purchase order.
po_edit_with_user = self.env.ref('purchase.purchase_order_5').sudo(self.res_users_purchase_user.id)
# Confirm the purchase order.
po_edit_with_user.button_confirm()
# Check the "Approved" status after confirmed RFQ.
self.assertEqual(po_edit_with_user.state, 'purchase', 'Purchase: PO state should be "Purchase')
# First cancel receptions related to this order if order shipped.
po_edit_with_user.picking_ids.action_cancel()
# Able to cancel purchase order.
po_edit_with_user.button_cancel()
# Check that order is cancelled.
self.assertEqual(po_edit_with_user.state, 'cancel', 'Purchase: PO state should be "Cancel')