Merge branch 'master-so/po-riddhi' into 'master'
Master so/po riddhi See merge request flectra-hq/flectra!78
This commit is contained in:
commit
ac31557348
4
addons/blanket_so_po/__init__.py
Executable file
4
addons/blanket_so_po/__init__.py
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import wizard
|
||||||
|
from . import models
|
26
addons/blanket_so_po/__manifest__.py
Normal file
26
addons/blanket_so_po/__manifest__.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Blanket Sale Order/Purchase Order',
|
||||||
|
'version': "1.0",
|
||||||
|
'category': 'Sales and Purchase Management',
|
||||||
|
'summary': 'A Blanket Sales/Purchase Order clearly lays out the terms '
|
||||||
|
'and conditions of a Sales/Purchase including quantities '
|
||||||
|
'required and when they are to be delivered.',
|
||||||
|
"author": "Flectra",
|
||||||
|
'website': 'https://flectrahq.com',
|
||||||
|
'depends': ['purchase', 'sale_stock'],
|
||||||
|
'data': [
|
||||||
|
'wizard/transfer_so_products_view.xml',
|
||||||
|
'wizard/transfer_po_products_view.xml',
|
||||||
|
'views/sale_view.xml',
|
||||||
|
'views/purchase_view.xml',
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
'demo/blanket_sale_demo.xml',
|
||||||
|
'demo/blanket_purchase_demo.xml'
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
41
addons/blanket_so_po/demo/blanket_purchase_demo.xml
Normal file
41
addons/blanket_so_po/demo/blanket_purchase_demo.xml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<flectra noupdate="1">
|
||||||
|
|
||||||
|
<record id="blanket_purchase_order" model="purchase.order">
|
||||||
|
<field name="partner_id" ref="base.res_partner_4"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="blanket_purchase_order_line_1" model="purchase.order.line">
|
||||||
|
<field name="order_id" ref="blanket_purchase_order"/>
|
||||||
|
<field name="name">[RAM-SR5] RAM DDR SR5</field>
|
||||||
|
<field name="date_planned" eval="time.strftime('%Y/%m/10')"/>
|
||||||
|
<field name="product_id" ref="product.product_product_6"/>
|
||||||
|
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||||
|
<field name="price_unit">6000</field>
|
||||||
|
<field name="product_qty">10</field>
|
||||||
|
<field name="blanket_po_line" eval="True"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="blanket_purchase_order_line_2" model="purchase.order.line">
|
||||||
|
<field name="order_id" ref="blanket_purchase_order"/>
|
||||||
|
<field name="name">[M-Wir] Mouse, Wireless]</field>
|
||||||
|
<field name="date_planned" eval="time.strftime('%Y/%m/10')"/>
|
||||||
|
<field name="product_id" ref="product.product_product_7"/>
|
||||||
|
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||||
|
<field name="price_unit">200</field>
|
||||||
|
<field name="product_qty">5</field>
|
||||||
|
<field name="blanket_po_line" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="blanket_purchase_order_line_3" model="purchase.order.line">
|
||||||
|
<field name="order_id" ref="blanket_purchase_order"/>
|
||||||
|
<field name="name">[MBi9] Motherboard I9P57</field>
|
||||||
|
<field name="date_planned" eval="time.strftime('%Y/%m/10')"/>
|
||||||
|
<field name="product_id" ref="product.product_product_8"/>
|
||||||
|
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||||
|
<field name="price_unit">3000</field>
|
||||||
|
<field name="product_qty">8</field>
|
||||||
|
<field name="blanket_po_line" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</flectra>
|
45
addons/blanket_so_po/demo/blanket_sale_demo.xml
Normal file
45
addons/blanket_so_po/demo/blanket_sale_demo.xml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<flectra noupdate="1">
|
||||||
|
|
||||||
|
<record id="sale_order_blanket1" model="sale.order">
|
||||||
|
<field name="partner_id" ref="base.res_partner_4"/>
|
||||||
|
<field name="partner_invoice_id" ref="base.res_partner_address_13"/>
|
||||||
|
<field name="partner_shipping_id" ref="base.res_partner_address_13"/>
|
||||||
|
<field name="user_id" ref="base.user_demo"/>
|
||||||
|
<field name="pricelist_id" ref="product.list0"/>
|
||||||
|
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||||
|
<field name="date_order"
|
||||||
|
eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sale_order_line_blanket_1" model="sale.order.line">
|
||||||
|
<field name="order_id" ref="sale_order_blanket1"/>
|
||||||
|
<field name="name">Laptop E5023</field>
|
||||||
|
<field name="product_id" ref="product.product_product_25"/>
|
||||||
|
<field name="product_uom_qty">10</field>
|
||||||
|
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||||
|
<field name="price_unit">2950.00</field>
|
||||||
|
<field name="blanket_so_line" eval="True"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sale_order_line_blanket_2" model="sale.order.line">
|
||||||
|
<field name="order_id" ref="sale_order_blanket1"/>
|
||||||
|
<field name="name">Headset USB</field>
|
||||||
|
<field name="product_id" ref="product.product_delivery_01"/>
|
||||||
|
<field name="product_uom_qty">3</field>
|
||||||
|
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||||
|
<field name="price_unit">2950.00</field>
|
||||||
|
<field name="blanket_so_line" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sale_order_line_blanket_3" model="sale.order.line">
|
||||||
|
<field name="order_id" ref="sale_order_blanket1"/>
|
||||||
|
<field name="name">Webcamv</field>
|
||||||
|
<field name="product_id" ref="product.product_delivery_02"/>
|
||||||
|
<field name="product_uom_qty">10</field>
|
||||||
|
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||||
|
<field name="price_unit">45.00</field>
|
||||||
|
<field name="blanket_so_line" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</flectra>
|
5
addons/blanket_so_po/models/__init__.py
Normal file
5
addons/blanket_so_po/models/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||||
|
# details.
|
||||||
|
|
||||||
|
from . import sale
|
||||||
|
from . import purchase
|
66
addons/blanket_so_po/models/purchase.py
Normal file
66
addons/blanket_so_po/models/purchase.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||||
|
# details.
|
||||||
|
|
||||||
|
from flectra import api, fields, models, _
|
||||||
|
from flectra.exceptions import Warning
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseOrder(models.Model):
|
||||||
|
_inherit = "purchase.order"
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def button_cancel(self):
|
||||||
|
res = super(PurchaseOrder, self).button_cancel()
|
||||||
|
if self.order_line.filtered(
|
||||||
|
lambda l: l.blanket_po_line):
|
||||||
|
raise Warning(
|
||||||
|
_('Sorry, You can not cancel blanket line based PO.'))
|
||||||
|
self.write({'state': 'cancel'})
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseOrderLine(models.Model):
|
||||||
|
_inherit = 'purchase.order.line'
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _prepare_stock_moves(self, picking):
|
||||||
|
res = super(PurchaseOrderLine, self)._prepare_stock_moves(picking)
|
||||||
|
self.ensure_one()
|
||||||
|
context = dict(self._context)
|
||||||
|
if self.product_id.type not in ['product',
|
||||||
|
'consu'] or self.blanket_po_line \
|
||||||
|
and not context.get('blanket'):
|
||||||
|
return []
|
||||||
|
qty = 0.0
|
||||||
|
for move in self.move_ids.filtered(
|
||||||
|
lambda x: x.state != 'cancel' and not
|
||||||
|
x.location_dest_id.usage == "supplier"):
|
||||||
|
qty += move.product_uom._compute_quantity(
|
||||||
|
move.product_uom_qty, self.product_uom,
|
||||||
|
rounding_method='HALF-UP')
|
||||||
|
for re in res:
|
||||||
|
if self.blanket_po_line and context.get('transfer_qty'):
|
||||||
|
re['product_uom_qty'] = context.get('transfer_qty')
|
||||||
|
else:
|
||||||
|
re['product_uom_qty'] = self.product_qty - qty
|
||||||
|
return res
|
||||||
|
|
||||||
|
blanket_po_line = fields.Boolean(string="Blanket Order", copy=False)
|
||||||
|
remaining_to_po_transfer = fields.Float(string="Remaining to Transfer",
|
||||||
|
copy=False)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def create(self, vals):
|
||||||
|
if vals.get('product_qty') and vals.get('blanket_po_line'):
|
||||||
|
vals.update(
|
||||||
|
{'remaining_to_po_transfer': vals.get('product_qty')})
|
||||||
|
res = super(PurchaseOrderLine, self).create(vals)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def write(self, values):
|
||||||
|
result = super(PurchaseOrderLine, self).write(values)
|
||||||
|
for line in self:
|
||||||
|
if 'product_qty' and 'blanket_po_line' in values:
|
||||||
|
line.remaining_to_po_transfer = line.product_qty
|
||||||
|
return result
|
114
addons/blanket_so_po/models/sale.py
Normal file
114
addons/blanket_so_po/models/sale.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||||
|
# details.
|
||||||
|
|
||||||
|
from flectra import api, models, fields
|
||||||
|
from flectra.exceptions import UserError
|
||||||
|
from flectra.tools import float_compare
|
||||||
|
|
||||||
|
|
||||||
|
class SaleOrder(models.Model):
|
||||||
|
_inherit = "sale.order"
|
||||||
|
|
||||||
|
|
||||||
|
class SaleOrderLine(models.Model):
|
||||||
|
_inherit = "sale.order.line"
|
||||||
|
_rec_name = 'product_id'
|
||||||
|
|
||||||
|
blanket_so_line = fields.Boolean(string="Blanket Order", copy=False)
|
||||||
|
remaining_to_so_transfer = fields.Float(string="Remaining to Transfer",
|
||||||
|
copy=False)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _action_launch_procurement_rule(self):
|
||||||
|
"""
|
||||||
|
Launch procurement group run method with required/custom fields
|
||||||
|
genrated by a
|
||||||
|
sale order line. procurement group will launch '_run_move',
|
||||||
|
'_run_buy' or '_run_manufacture'
|
||||||
|
depending on the sale order line product rule.
|
||||||
|
"""
|
||||||
|
precision = self.env['decimal.precision'].precision_get(
|
||||||
|
'Product Unit of Measure')
|
||||||
|
errors = []
|
||||||
|
context = dict(self._context)
|
||||||
|
for line in self:
|
||||||
|
if line.state != 'sale' or not line.product_id.type in (
|
||||||
|
'consu', 'product') or line.blanket_so_line and \
|
||||||
|
not context.get('blanket'):
|
||||||
|
continue
|
||||||
|
qty = 0.0
|
||||||
|
for move in line.move_ids.filtered(lambda r: r.state != 'cancel'):
|
||||||
|
qty += move.product_uom._compute_quantity(move.product_uom_qty,
|
||||||
|
line.product_uom,
|
||||||
|
rounding_method='HALF-UP')
|
||||||
|
if float_compare(qty, line.product_uom_qty,
|
||||||
|
precision_digits=precision) >= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
group_id = line.order_id.procurement_group_id
|
||||||
|
if not group_id:
|
||||||
|
group_id = self.env['procurement.group'].create({
|
||||||
|
'name': line.order_id.name,
|
||||||
|
'move_type': line.order_id.picking_policy,
|
||||||
|
'sale_id': line.order_id.id,
|
||||||
|
'partner_id': line.order_id.partner_shipping_id.id,
|
||||||
|
})
|
||||||
|
line.order_id.procurement_group_id = group_id
|
||||||
|
else:
|
||||||
|
# In case the procurement group is already created and the
|
||||||
|
# order was
|
||||||
|
# cancelled, we need to update certain values of the group.
|
||||||
|
updated_vals = {}
|
||||||
|
if group_id.partner_id != line.order_id.partner_shipping_id:
|
||||||
|
updated_vals.update(
|
||||||
|
{'partner_id': line.order_id.partner_shipping_id.id})
|
||||||
|
if group_id.move_type != line.order_id.picking_policy:
|
||||||
|
updated_vals.update(
|
||||||
|
{'move_type': line.order_id.picking_policy})
|
||||||
|
if updated_vals:
|
||||||
|
group_id.write(updated_vals)
|
||||||
|
|
||||||
|
values = line._prepare_procurement_values(group_id=group_id)
|
||||||
|
|
||||||
|
if line.blanket_so_line and context.get('blanket'):
|
||||||
|
product_qty = context.get('transfer_qty')
|
||||||
|
else:
|
||||||
|
product_qty = line.product_uom_qty - qty
|
||||||
|
|
||||||
|
procurement_uom = line.product_uom
|
||||||
|
quant_uom = line.product_id.uom_id
|
||||||
|
get_param = self.env['ir.config_parameter'].sudo().get_param
|
||||||
|
if procurement_uom.id != quant_uom.id and get_param(
|
||||||
|
'stock.propagate_uom') != '1':
|
||||||
|
product_qty = line.product_uom._compute_quantity(product_qty,
|
||||||
|
quant_uom,
|
||||||
|
rounding_method='HALF-UP')
|
||||||
|
procurement_uom = quant_uom
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.env['procurement.group'].run(line.product_id, product_qty,
|
||||||
|
procurement_uom,
|
||||||
|
line.order_id.partner_shipping_id.property_stock_customer,
|
||||||
|
line.name,
|
||||||
|
line.order_id.name, values)
|
||||||
|
except UserError as error:
|
||||||
|
errors.append(error.name)
|
||||||
|
if errors:
|
||||||
|
raise UserError('\n'.join(errors))
|
||||||
|
return True
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def create(self, vals):
|
||||||
|
if vals.get('product_uom_qty') and vals.get('blanket_so_line'):
|
||||||
|
vals.update(
|
||||||
|
{'remaining_to_so_transfer': vals.get('product_uom_qty')})
|
||||||
|
res = super(SaleOrderLine, self).create(vals)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def write(self, values):
|
||||||
|
result = super(SaleOrderLine, self).write(values)
|
||||||
|
for line in self:
|
||||||
|
if 'product_uom_qty' and 'blanket_so_line' in values:
|
||||||
|
line.remaining_to_so_transfer = line.product_uom_qty
|
||||||
|
return result
|
BIN
addons/blanket_so_po/static/description/icon.png
Normal file
BIN
addons/blanket_so_po/static/description/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
4
addons/blanket_so_po/tests/__init__.py
Normal file
4
addons/blanket_so_po/tests/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import test_purchase_to_invoice_and_delivery
|
||||||
|
from . import test_sale_to_invoice_and_delivery
|
@ -0,0 +1,175 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||||
|
# details.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from flectra.exceptions import Warning
|
||||||
|
from flectra.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestPurchaseOrder(TransactionCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestPurchaseOrder, self).setUp()
|
||||||
|
self.stock_move = self.env['stock.move']
|
||||||
|
self.inv_obj = self.env['account.invoice']
|
||||||
|
self.purchase_wizard = self.env['purchase.transfer.products']
|
||||||
|
self.po1 = self.env.ref('blanket_so_po.blanket_purchase_order')
|
||||||
|
self.po_line_with_blanket = self.env.ref(
|
||||||
|
'blanket_so_po.blanket_purchase_order_line_1')
|
||||||
|
self.po_line_without_blanket = self.env.ref(
|
||||||
|
'blanket_so_po.blanket_purchase_order_line_2')
|
||||||
|
self.po_line_without_blanket2 = self.env.ref(
|
||||||
|
'blanket_so_po.blanket_purchase_order_line_3')
|
||||||
|
|
||||||
|
def test_2_purchase_with_blanket(self):
|
||||||
|
self.assertTrue(self.po1, 'Purchase: no purchase order created')
|
||||||
|
self.po1.button_confirm()
|
||||||
|
self.assertEqual(self.po1.state, 'purchase',
|
||||||
|
'Purchase: PO state should be "Purchase"')
|
||||||
|
self.assertTrue(self.po1.picking_ids, "Picking should be created.")
|
||||||
|
logging.info('Test Cases for Blanket Purchase order')
|
||||||
|
logging.info('Purchase Order - %s' % (self.po1.name))
|
||||||
|
logging.info(
|
||||||
|
'============================================================='
|
||||||
|
'==================+=====')
|
||||||
|
logging.info(
|
||||||
|
' | Blanket Po Line | Product | Ordered Qty | Remaining '
|
||||||
|
'to transfer | Received Qty |')
|
||||||
|
for line in self.po1.order_line:
|
||||||
|
logging.info(
|
||||||
|
' %s | %s | %d | %d '
|
||||||
|
' | %d ' % (
|
||||||
|
line.blanket_po_line, line.product_id.name,
|
||||||
|
line.product_qty,
|
||||||
|
line.remaining_to_po_transfer, line.qty_received))
|
||||||
|
logging.info(
|
||||||
|
'========================================================='
|
||||||
|
'========================')
|
||||||
|
#
|
||||||
|
blanket_lines_po = self.po1.order_line.search(
|
||||||
|
[('order_id', '=', self.po1.id),
|
||||||
|
('blanket_po_line', '=', True)])
|
||||||
|
uom_qty = self.po_line_with_blanket.product_qty
|
||||||
|
transfer_qty = 2
|
||||||
|
remaining_qty = 0.0
|
||||||
|
|
||||||
|
len_blanket_lines = len(blanket_lines_po)
|
||||||
|
total_po_line = len(self.po1.order_line)
|
||||||
|
move_lines = len(self.po1.picking_ids.move_lines)
|
||||||
|
logging.info(
|
||||||
|
'*****************************************************')
|
||||||
|
logging.info('Delivery Order for Purchase Order- %s' % (
|
||||||
|
self.po1.picking_ids.name))
|
||||||
|
logging.info(
|
||||||
|
'============================================================'
|
||||||
|
'===================+=====')
|
||||||
|
logging.info(
|
||||||
|
' | Product | Initial Demand | Reserved | Done |')
|
||||||
|
for move in self.po1.picking_ids.move_lines:
|
||||||
|
logging.info('| %s | %d |%d | %d' % (
|
||||||
|
move.product_id.name, move.product_uom_qty,
|
||||||
|
move.reserved_availability, move.quantity_done))
|
||||||
|
logging.info(
|
||||||
|
'========================================================'
|
||||||
|
'=========================')
|
||||||
|
self.assertEqual(move_lines, total_po_line - len_blanket_lines,
|
||||||
|
'There is no equal number of move lines in move')
|
||||||
|
|
||||||
|
self.assertTrue(self.po_line_with_blanket.blanket_po_line,
|
||||||
|
'Purchase: There is a Blanket po line')
|
||||||
|
|
||||||
|
remaining_qty = uom_qty - transfer_qty
|
||||||
|
transfer_wizard = self.purchase_wizard.create(
|
||||||
|
{'ref_id': self.po_line_with_blanket.id,
|
||||||
|
'transfer_qty': transfer_qty})
|
||||||
|
transfer_wizard.split_qty_wt_newline_po()
|
||||||
|
self.assertEqual(remaining_qty, uom_qty - transfer_qty,
|
||||||
|
'Remaining to transfer qty is different')
|
||||||
|
|
||||||
|
total_po_line = len(self.po1.order_line)
|
||||||
|
|
||||||
|
transfer_qty += 5
|
||||||
|
remaining_qty = uom_qty - transfer_qty
|
||||||
|
transfer_wizard = self.purchase_wizard.create(
|
||||||
|
{'ref_id': self.po_line_with_blanket.id, 'transfer_qty': 5})
|
||||||
|
transfer_wizard.split_qty_wt_newline_po()
|
||||||
|
self.assertEqual(remaining_qty, uom_qty - transfer_qty,
|
||||||
|
'Remaining to transfer qty is different')
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
'*****************************************************')
|
||||||
|
logging.info(
|
||||||
|
'Purchase Order after Blanket Split lines- %s' % (
|
||||||
|
self.po1.name))
|
||||||
|
logging.info(
|
||||||
|
'============================================================'
|
||||||
|
'===================+=====')
|
||||||
|
logging.info(
|
||||||
|
' | Blanket Po Line | Product | Ordered Qty | Remaining '
|
||||||
|
'to transfer | Received Qty |')
|
||||||
|
for line in self.po1.order_line:
|
||||||
|
logging.info(
|
||||||
|
' %s | %s | %d | %d '
|
||||||
|
' | %d ' % (
|
||||||
|
line.blanket_po_line, line.product_id.name,
|
||||||
|
line.product_qty,
|
||||||
|
line.remaining_to_po_transfer, line.qty_received))
|
||||||
|
logging.info(
|
||||||
|
'========================================================='
|
||||||
|
'========================')
|
||||||
|
|
||||||
|
with self.assertRaises(Warning):
|
||||||
|
self.po1.button_cancel()
|
||||||
|
|
||||||
|
self.picking = self.po1.picking_ids[0]
|
||||||
|
self.assertEqual(self.picking.move_lines[-1].quantity_done, 0.0)
|
||||||
|
self.assertEqual(self.picking.move_lines[-2].quantity_done, 0.0)
|
||||||
|
self.picking.move_lines[-1].quantity_done = 2
|
||||||
|
|
||||||
|
self.po1._compute_picking()
|
||||||
|
self.po1._compute_is_shipped()
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
'*****************************************************')
|
||||||
|
logging.info('Delivery Order - %s' % (self.picking.name))
|
||||||
|
logging.info(
|
||||||
|
'========================================================='
|
||||||
|
'======================+=====')
|
||||||
|
logging.info(
|
||||||
|
' id | Product | Initial Demand | Reserved | Done |')
|
||||||
|
for move in self.picking.move_lines:
|
||||||
|
logging.info(
|
||||||
|
'%d | %s | %d |%d | %d' % (
|
||||||
|
move.id, move.product_id.name, move.product_uom_qty,
|
||||||
|
move.reserved_availability, move.quantity_done))
|
||||||
|
logging.info(
|
||||||
|
'========================================================='
|
||||||
|
'========================')
|
||||||
|
|
||||||
|
self.picking.force_assign()
|
||||||
|
res_dict = self.picking.button_validate()
|
||||||
|
backorder_wizard = self.env[(res_dict.get('res_model'))].browse(
|
||||||
|
res_dict.get('res_id'))
|
||||||
|
backorder_wizard.process()
|
||||||
|
self.picking.action_done()
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
'*****************************************************')
|
||||||
|
logging.info('Purchase Order after validate Delivery order- %s' % (
|
||||||
|
self.po1.name))
|
||||||
|
logging.info(
|
||||||
|
'============================================================'
|
||||||
|
'===================+=====')
|
||||||
|
logging.info(
|
||||||
|
' | Blanket Po Line | Product | Ordered Qty | Remaining '
|
||||||
|
'to transfer | Received Qty |')
|
||||||
|
for line in self.po1.order_line:
|
||||||
|
logging.info(
|
||||||
|
' %s | %s | %d | %d '
|
||||||
|
' | %d ' % (
|
||||||
|
line.blanket_po_line, line.product_id.name,
|
||||||
|
line.product_qty,
|
||||||
|
line.remaining_to_po_transfer, line.qty_received))
|
||||||
|
logging.info(
|
||||||
|
'========================================================='
|
||||||
|
'========================')
|
180
addons/blanket_so_po/tests/test_sale_to_invoice_and_delivery.py
Normal file
180
addons/blanket_so_po/tests/test_sale_to_invoice_and_delivery.py
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||||
|
# details.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from flectra.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestSaleOrder(TransactionCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestSaleOrder, self).setUp()
|
||||||
|
self.so_model = self.env['sale.order']
|
||||||
|
self.so_line_model = self.env['sale.order.line']
|
||||||
|
self.stock_picking_model = self.env['stock.picking']
|
||||||
|
self.stock_move_model = self.env['stock.move']
|
||||||
|
self.stock_location_model = self.env['stock.location']
|
||||||
|
self.sale_wizard = self.env['sale.transfer.products']
|
||||||
|
self.invoice_wizard = self.env['sale.advance.payment.inv']
|
||||||
|
self.sale1 = self.env.ref('blanket_so_po.sale_order_blanket1')
|
||||||
|
self.so_line_with_blanket = self.env.ref(
|
||||||
|
'blanket_so_po.sale_order_line_blanket_1')
|
||||||
|
self.so_line_without_blanket = self.env.ref(
|
||||||
|
'blanket_so_po.sale_order_line_blanket_2')
|
||||||
|
self.so_line_without_blanket2 = self.env.ref(
|
||||||
|
'blanket_so_po.sale_order_line_blanket_3')
|
||||||
|
self.inv_obj = self.env['account.invoice']
|
||||||
|
|
||||||
|
def test_1_sale_with_blanket(self):
|
||||||
|
self.sale1.force_quotation_send()
|
||||||
|
self.sale1.action_confirm()
|
||||||
|
self.assertTrue(self.sale1.state, 'sale')
|
||||||
|
self.assertTrue(self.sale1.invoice_status, 'to invoice')
|
||||||
|
logging.info('Test Cases for Blanket Sale order')
|
||||||
|
logging.info('Sale Order - %s' % (self.sale1.name))
|
||||||
|
logging.info(
|
||||||
|
'============================================================'
|
||||||
|
'===================+=====')
|
||||||
|
logging.info(
|
||||||
|
' | Blanket So Line | Product | Ordered Qty | Remaining '
|
||||||
|
'to transfer | Delivered |')
|
||||||
|
for line in self.sale1.order_line:
|
||||||
|
logging.info(
|
||||||
|
' %s | %s | %d | %d '
|
||||||
|
' | %d ' % (
|
||||||
|
line.blanket_so_line, line.product_id.name,
|
||||||
|
line.product_uom_qty, line.remaining_to_so_transfer,
|
||||||
|
line.qty_delivered))
|
||||||
|
logging.info(
|
||||||
|
'========================================================'
|
||||||
|
'=========================')
|
||||||
|
uom_qty = self.so_line_with_blanket.product_uom_qty
|
||||||
|
transfer_qty = 2
|
||||||
|
remaining_qty = 0.0
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
'*****************************************************')
|
||||||
|
logging.info(
|
||||||
|
'========================================================='
|
||||||
|
'======================+=====')
|
||||||
|
logging.info(
|
||||||
|
' | Product | Initial Demand | Reserved | Done |')
|
||||||
|
for picking in self.sale1.picking_ids:
|
||||||
|
for line in picking.move_lines:
|
||||||
|
logging.info('| %s | %d |%d | %d' % (
|
||||||
|
line.product_id.name, line.product_uom_qty,
|
||||||
|
line.reserved_availability, line.quantity_done))
|
||||||
|
logging.info(
|
||||||
|
'======================================================='
|
||||||
|
'==========================')
|
||||||
|
|
||||||
|
remaining_qty = uom_qty - transfer_qty
|
||||||
|
transfer_wizard = self.sale_wizard.create(
|
||||||
|
{'ref_id': self.so_line_with_blanket.id,
|
||||||
|
'transfer_qty': transfer_qty})
|
||||||
|
transfer_wizard.split_qty_wt_newline()
|
||||||
|
self.assertEqual(remaining_qty, uom_qty - transfer_qty,
|
||||||
|
'Remaining to transfer qty is different')
|
||||||
|
|
||||||
|
transfer_qty += 5
|
||||||
|
remaining_qty = uom_qty - transfer_qty
|
||||||
|
transfer_wizard = self.sale_wizard.create(
|
||||||
|
{'ref_id': self.so_line_with_blanket.id, 'transfer_qty': 5})
|
||||||
|
transfer_wizard.split_qty_wt_newline()
|
||||||
|
self.assertEqual(remaining_qty, uom_qty - transfer_qty,
|
||||||
|
'Remaining to transfer qty is different')
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
'*****************************************************')
|
||||||
|
logging.info(
|
||||||
|
'Sale Order after Blanket Split lines- %s' % (self.sale1.name))
|
||||||
|
logging.info(
|
||||||
|
'========================================================='
|
||||||
|
'======================+=====')
|
||||||
|
logging.info(
|
||||||
|
' | Blanket So Line | Product | Ordered Qty | Remaining '
|
||||||
|
'to transfer | Delivered |')
|
||||||
|
for line in self.sale1.order_line:
|
||||||
|
logging.info(
|
||||||
|
' %s | %s | %d | %d '
|
||||||
|
' | %d ' % (
|
||||||
|
line.blanket_so_line, line.product_id.name,
|
||||||
|
line.product_uom_qty, line.remaining_to_so_transfer,
|
||||||
|
line.qty_delivered))
|
||||||
|
logging.info(
|
||||||
|
'======================================================='
|
||||||
|
'==========================')
|
||||||
|
|
||||||
|
self.assertEqual(self.sale1.picking_ids.move_lines[-1].quantity_done,
|
||||||
|
0.0)
|
||||||
|
self.assertEqual(self.sale1.picking_ids.move_lines[-2].quantity_done,
|
||||||
|
0.0)
|
||||||
|
self.sale1.picking_ids.move_lines[-1].quantity_done = 2
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
'*****************************************************')
|
||||||
|
logging.info('Delivery Order - %s' % (self.sale1.picking_ids.name))
|
||||||
|
logging.info(
|
||||||
|
'==========================================================='
|
||||||
|
'====================+=====')
|
||||||
|
logging.info(
|
||||||
|
' | Product | Initial Demand | Reserved | Done |')
|
||||||
|
for move in self.sale1.picking_ids.move_lines:
|
||||||
|
logging.info('| %s | %d |%d | %d' % (
|
||||||
|
move.product_id.name, move.product_uom_qty,
|
||||||
|
move.reserved_availability, move.quantity_done))
|
||||||
|
logging.info(
|
||||||
|
'======================================================='
|
||||||
|
'==========================')
|
||||||
|
|
||||||
|
self.sale1.picking_ids.action_confirm()
|
||||||
|
self.sale1.picking_ids.action_assign()
|
||||||
|
res_dict = self.sale1.picking_ids.button_validate()
|
||||||
|
backorder_wizard = self.env[(res_dict.get('res_model'))].browse(
|
||||||
|
res_dict.get('res_id'))
|
||||||
|
backorder_wizard.process()
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
'*****************************************************')
|
||||||
|
logging.info(
|
||||||
|
'Sale Order after validate Delivery order- %s' % (
|
||||||
|
self.sale1.name))
|
||||||
|
logging.info(
|
||||||
|
'=========================================================='
|
||||||
|
'=====================+=====')
|
||||||
|
logging.info(
|
||||||
|
' | Blanket So Line | Product | Ordered Qty | Remaining '
|
||||||
|
'to transfer | Delivered |invoice status')
|
||||||
|
for line in self.sale1.order_line:
|
||||||
|
logging.info(
|
||||||
|
' %s | %s | %d | %d '
|
||||||
|
' | %d |%s' % (
|
||||||
|
line.blanket_so_line, line.product_id.name,
|
||||||
|
line.product_uom_qty, line.remaining_to_so_transfer,
|
||||||
|
line.qty_delivered, line.invoice_status))
|
||||||
|
logging.info(
|
||||||
|
'======================================================='
|
||||||
|
'==========================')
|
||||||
|
|
||||||
|
self.assertEqual(len(self.sale1.picking_ids), 2,
|
||||||
|
'There is no 2 pickings are available')
|
||||||
|
|
||||||
|
context = {"active_model": 'sale.order',
|
||||||
|
"active_ids": [self.sale1.id],
|
||||||
|
"active_id": self.sale1.id}
|
||||||
|
|
||||||
|
for invoice in self.sale1.invoice_ids:
|
||||||
|
logging.info('Invoice of Delivered Quantity.')
|
||||||
|
for line in invoice.invoice_line_ids:
|
||||||
|
logging.info(
|
||||||
|
'==================================================')
|
||||||
|
logging.info(
|
||||||
|
'| Product | Quantity | Unit Price | Subtotal |')
|
||||||
|
logging.info(
|
||||||
|
'=================================================|')
|
||||||
|
logging.info(
|
||||||
|
'|%s |%d |%d |%d | ' % (
|
||||||
|
line.product_id.name, line.quantity,
|
||||||
|
line.price_unit,
|
||||||
|
line.price_subtotal))
|
||||||
|
invoice.with_context(context).invoice_validate()
|
51
addons/blanket_so_po/views/purchase_view.xml
Executable file
51
addons/blanket_so_po/views/purchase_view.xml
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<flectra>
|
||||||
|
|
||||||
|
<record id="inherit_blanket_purchase_order_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">inherit.purchase.form.view</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||||
|
<field name="priority">1</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/form/sheet/group"
|
||||||
|
position="before">
|
||||||
|
<header>
|
||||||
|
<button name="%(action_purchase_transfer_products)d"
|
||||||
|
type="action" string="Transfer"
|
||||||
|
class="oe_highlight"
|
||||||
|
attrs="{'invisible':['|', '|', ('blanket_po_line', '=', False), ('remaining_to_po_transfer','<=',0),('state','!=','purchase')]}"/>
|
||||||
|
</header>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/form/sheet/group/group/div/field[@name='product_uom']"
|
||||||
|
position="before">
|
||||||
|
<field name="state" invisible="1"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='order_line']/tree"
|
||||||
|
position="attributes">
|
||||||
|
<attribute name="editable"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='name']"
|
||||||
|
position="after">
|
||||||
|
<field name='blanket_po_line'/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/form/sheet/group/group/field[@name='taxes_id']"
|
||||||
|
position="before">
|
||||||
|
<field name='blanket_po_line'
|
||||||
|
attrs="{'readonly':[('state','=','sale')]}"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/form/sheet/group/group/field[@name='product_id']"
|
||||||
|
position="after">
|
||||||
|
<field name="remaining_to_po_transfer"
|
||||||
|
attrs="{'invisible':[('state','!=','purchase')]}"
|
||||||
|
string="Remaining to Transfer"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='qty_received']"
|
||||||
|
position="after">
|
||||||
|
<field name="remaining_to_po_transfer"
|
||||||
|
string="Remaining to Transfer"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</flectra>
|
47
addons/blanket_so_po/views/sale_view.xml
Executable file
47
addons/blanket_so_po/views/sale_view.xml
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<flectra>
|
||||||
|
|
||||||
|
<record id="inherit_blanket_sale_order_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">inherit.sale.order.form.view</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
|
<field name="priority">1</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='name']"
|
||||||
|
position="after">
|
||||||
|
<field name='blanket_so_line'/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/form/group/group/field[@name='tax_id']"
|
||||||
|
position="before">
|
||||||
|
<field name="blanket_so_line"
|
||||||
|
attrs="{'readonly':[('state','=','sale')]}"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='order_line']/form/group"
|
||||||
|
position="before">
|
||||||
|
<header>
|
||||||
|
<button name="%(action_sale_transfer_products)d"
|
||||||
|
type="action" string="Transfer"
|
||||||
|
class="oe_highlight"
|
||||||
|
attrs="{'invisible':['|', '|', ('blanket_so_line', '=', False), ('remaining_to_so_transfer','<=',0),('state','!=','sale')]}"/>
|
||||||
|
</header>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='order_line']/tree"
|
||||||
|
position="attributes">
|
||||||
|
<attribute name="editable"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/form/group/group/label[@for='product_uom_qty']"
|
||||||
|
position="before">
|
||||||
|
<field name="remaining_to_so_transfer"
|
||||||
|
attrs="{'invisible':[('state','!=','sale')]}"
|
||||||
|
string="Remaining to Transfer"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="/form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_uom_qty']"
|
||||||
|
position="after">
|
||||||
|
<field name="remaining_to_so_transfer"
|
||||||
|
string="Remaining to Transfer"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</flectra>
|
6
addons/blanket_so_po/wizard/__init__.py
Executable file
6
addons/blanket_so_po/wizard/__init__.py
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||||
|
# details.
|
||||||
|
|
||||||
|
|
||||||
|
from . import transfer_po_products
|
||||||
|
from . import transfer_so_products
|
56
addons/blanket_so_po/wizard/transfer_po_products.py
Normal file
56
addons/blanket_so_po/wizard/transfer_po_products.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||||
|
# details.
|
||||||
|
|
||||||
|
from flectra import api, fields, models, _
|
||||||
|
from flectra.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseTransferProducts(models.TransientModel):
|
||||||
|
_name = 'purchase.transfer.products'
|
||||||
|
_description = 'Transfer products from Purchase Lines'
|
||||||
|
|
||||||
|
transfer_qty = fields.Float("Transfer Qty")
|
||||||
|
ref_id = fields.Many2one('purchase.order.line',
|
||||||
|
string="Product Reference",
|
||||||
|
readonly=True)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def default_get(self, fields):
|
||||||
|
context = dict(self._context)
|
||||||
|
result = super(PurchaseTransferProducts, self).default_get(
|
||||||
|
fields)
|
||||||
|
purchase_line = self.env['purchase.order.line'].browse(
|
||||||
|
context.get('active_id'))
|
||||||
|
result.update({
|
||||||
|
'ref_id': purchase_line.id or False,
|
||||||
|
'transfer_qty': purchase_line.remaining_to_po_transfer})
|
||||||
|
return result
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def split_qty_wt_newline_po(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.ref_id.remaining_to_po_transfer < self.transfer_qty:
|
||||||
|
raise ValidationError(_(
|
||||||
|
'Sorry, You can not transfer more than requested or '
|
||||||
|
'remains!'))
|
||||||
|
elif self.transfer_qty <= 0:
|
||||||
|
raise ValidationError(_(
|
||||||
|
'Sorry, You can not transfer zero or negative '
|
||||||
|
'quantity!'))
|
||||||
|
self.ref_id._create_or_update_picking()
|
||||||
|
pickings = self.ref_id.order_id.picking_ids.filtered(
|
||||||
|
lambda p: p.state not in ('done', 'cancel'))
|
||||||
|
self.ref_id.remaining_to_po_transfer -= self.transfer_qty
|
||||||
|
for picking in pickings:
|
||||||
|
for line in picking.move_lines.filtered(
|
||||||
|
lambda l: l.product_id.id == self.ref_id.product_id.id and
|
||||||
|
l.purchase_line_id.id == self.ref_id.id):
|
||||||
|
total_product_qty = \
|
||||||
|
self.ref_id.remaining_to_po_transfer + \
|
||||||
|
self.ref_id.qty_received
|
||||||
|
line.product_uom_qty = \
|
||||||
|
self.ref_id.product_qty - total_product_qty
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'reload',
|
||||||
|
}
|
39
addons/blanket_so_po/wizard/transfer_po_products_view.xml
Executable file
39
addons/blanket_so_po/wizard/transfer_po_products_view.xml
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<flectra>
|
||||||
|
|
||||||
|
<record id="purchase_transfer_products_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">purchase.transfer.products.form.view</field>
|
||||||
|
<field name="model">purchase.transfer.products</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="ref_id"/>
|
||||||
|
<field name="transfer_qty" default_focus="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button name="split_qty_wt_newline_po" string="Transfer"
|
||||||
|
type="object" class="oe_highlight"
|
||||||
|
context="{'blanket':True, 'transfer_qty':transfer_qty}"/>
|
||||||
|
or
|
||||||
|
<button special="cancel" string="Cancel"
|
||||||
|
class="oe_highlight"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_purchase_transfer_products"
|
||||||
|
model="ir.actions.act_window">
|
||||||
|
<field name="name">Transfer Products</field>
|
||||||
|
<field name="res_model">purchase.transfer.products</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="purchase_transfer_products_form_view"/>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</flectra>
|
||||||
|
|
52
addons/blanket_so_po/wizard/transfer_so_products.py
Normal file
52
addons/blanket_so_po/wizard/transfer_so_products.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||||
|
# details.
|
||||||
|
|
||||||
|
from flectra import api, fields, models, _
|
||||||
|
from flectra.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class SaleTransferProducts(models.TransientModel):
|
||||||
|
_name = 'sale.transfer.products'
|
||||||
|
_description = 'Transfer products from Sale Lines'
|
||||||
|
|
||||||
|
transfer_qty = fields.Float("Transfer Qty")
|
||||||
|
ref_id = fields.Many2one('sale.order.line', string="Product Reference",
|
||||||
|
readonly=True)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def default_get(self, fields):
|
||||||
|
context = dict(self._context)
|
||||||
|
result = super(SaleTransferProducts, self).default_get(fields)
|
||||||
|
sale_line = self.env['sale.order.line'].browse(
|
||||||
|
context.get('active_id'))
|
||||||
|
result.update({
|
||||||
|
'ref_id': sale_line.id or False,
|
||||||
|
'transfer_qty': sale_line.remaining_to_so_transfer})
|
||||||
|
return result
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def split_qty_wt_newline(self):
|
||||||
|
if self.ref_id.remaining_to_so_transfer < self.transfer_qty:
|
||||||
|
raise ValidationError(_(
|
||||||
|
'Sorry, You can not transfer more than requested or '
|
||||||
|
'remains!'))
|
||||||
|
elif self.transfer_qty <= 0:
|
||||||
|
raise ValidationError(_(
|
||||||
|
'Sorry, You can not transfer zero or negative '
|
||||||
|
'quantity!'))
|
||||||
|
self.ref_id._action_launch_procurement_rule()
|
||||||
|
pickings = self.ref_id.order_id.picking_ids.filtered(
|
||||||
|
lambda p: p.state not in ('done', 'cancel'))
|
||||||
|
self.ref_id.remaining_to_so_transfer -= self.transfer_qty
|
||||||
|
for picking in pickings:
|
||||||
|
for line in picking.move_lines.filtered(
|
||||||
|
lambda l: l.product_id.id == self.ref_id.product_id.id and
|
||||||
|
l.sale_line_id.id == self.ref_id.id):
|
||||||
|
total_product_qty = self.ref_id.remaining_to_so_transfer \
|
||||||
|
+ self.ref_id.qty_delivered
|
||||||
|
line.product_uom_qty = self.ref_id.product_uom_qty - \
|
||||||
|
total_product_qty
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'reload',
|
||||||
|
}
|
38
addons/blanket_so_po/wizard/transfer_so_products_view.xml
Executable file
38
addons/blanket_so_po/wizard/transfer_so_products_view.xml
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<flectra>
|
||||||
|
|
||||||
|
<record id="sale_transfer_products_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">sale.transfer.products.form.view</field>
|
||||||
|
<field name="model">sale.transfer.products</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="ref_id"/>
|
||||||
|
<field name="transfer_qty" default_focus="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button name="split_qty_wt_newline" string="Transfer"
|
||||||
|
type="object" class="oe_highlight"
|
||||||
|
context="{'blanket':True, 'transfer_qty':transfer_qty}"/>
|
||||||
|
or
|
||||||
|
<button special="cancel" string="Cancel"
|
||||||
|
class="oe_highlight"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sale_transfer_products"
|
||||||
|
model="ir.actions.act_window">
|
||||||
|
<field name="name">Transfer Products</field>
|
||||||
|
<field name="res_model">sale.transfer.products</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="sale_transfer_products_form_view"/>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</flectra>
|
@ -195,7 +195,7 @@ class TestStockValuation(TransactionCase):
|
|||||||
wizard.process()
|
wizard.process()
|
||||||
|
|
||||||
# the unit price of the stock move has been updated to the latest value
|
# the unit price of the stock move has been updated to the latest value
|
||||||
self.assertEquals(move1.price_unit, price_unit_usd_new_rate)
|
self.assertEquals(round(move1.price_unit), round(price_unit_usd_new_rate))
|
||||||
|
|
||||||
self.assertAlmostEqual(self.product1.stock_value, price_unit_usd_new_rate * 10, delta=0.1)
|
self.assertAlmostEqual(self.product1.stock_value, price_unit_usd_new_rate * 10, delta=0.1)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user