Merge pull request #1124 from odoo-dev/master-wmsstaging4-jco

fixes related to WMS, product variants and templates... from Josse
This commit is contained in:
qdp-odoo 2014-07-17 18:18:16 +02:00
commit d35d47165e
43 changed files with 430 additions and 312 deletions

View File

@ -1925,7 +1925,7 @@
<field name="arch" type="xml">
<search string="Search Account Templates">
<field name="name" filter_domain="['|', ('name','ilike',self), ('code','ilike',self)]" string="Account Template"/>
<filter icon="terp-sale" string="Receivale Accounts" domain="[('type','=','receivable')]"/>
<filter icon="terp-sale" string="Receivable Accounts" domain="[('type','=','receivable')]"/>
<filter icon="terp-purchase" string="Payable Accounts" domain="[('type','=','payable')]"/>
<field name="parent_id"/>
<field name="user_type"/>

View File

@ -130,4 +130,27 @@ class product_product(osv.Model):
'rules_count': fields.function(_rules_count, string='# Analytic Rules', type='integer'),
}
class product_template(osv.Model):
_inherit = 'product.template'
def _rules_count(self, cr, uid, ids, field_name, arg, context=None):
Analytic = self.pool['account.analytic.default']
res = {}
for product_tmpl_id in self.browse(cr, uid, ids, context=context):
res[product_tmpl_id.id] = sum([p.rules_count for p in product_tmpl_id.product_variant_ids])
return res
_columns = {
'rules_count': fields.function(_rules_count, string='# Analytic Rules', type='integer'),
}
def action_view_rules(self, cr, uid, ids, context=None):
products = self._get_products(cr, uid, ids, context=context)
result = self._get_act_window_dict(cr, uid, 'account_analytic_default.action_product_default_list', context=context)
result['domain'] = "[('product_id','in',[" + ','.join(map(str, products)) + "])]"
# Remove context so it is not going to filter on product_id with active_id of template
result['context'] = "{}"
return result
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -87,6 +87,20 @@
</xpath>
</field>
</record>
<record model="ir.ui.view" id="product_template_view_default_analytic_button">
<field name="name">product.template.stock.move</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" name= "action_view_rules" type="object"
groups="analytic.group_analytic_accounting" icon="fa-xing">
<field string="Analytic Rules" name="rules_count" widget="statinfo" />
</button>
</xpath>
</field>
</record>
<act_window
name="Entries"

View File

@ -124,7 +124,7 @@
-
!workflow {model: purchase.order, ref: purchase_order_001, action: purchase_confirm}
-
Reception is ready for process so now done the reception.
Receipt is ready for process so now we do it.
-
!python {model: stock.picking}: |
picking_id = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001"), context=context).picking_ids[0]

View File

@ -132,7 +132,7 @@
-
!workflow {model: purchase.order, ref: purchase_order_001_fifo, action: purchase_confirm}
-
Reception is ready for process so now done the reception.
Receipt is ready for process so now we do it.
-
!python {model: stock.picking}: |
picking_id = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001_fifo"), context=context).picking_ids[0]

View File

@ -745,6 +745,8 @@ class calendar_event(osv.Model):
def _compute(self, cr, uid, ids, fields, arg, context=None):
res = {}
if not isinstance(fields, list):
fields = [fields]
for meeting_id in ids:
res[meeting_id] = {}
attendee = self._find_my_attendee(cr, uid, [meeting_id], context)

View File

@ -152,7 +152,7 @@ class mail_notification(osv.Model):
sent_by = _('Sent from %(company)s using %(openerp)s')
signature_company = '<small>%s</small>' % (sent_by % {
'company': company,
'openerp': "<a style='color:inherit' href='https://www.openerp.com/'>OpenERP</a>"
'openerp': "<a style='color:inherit' href='https://www.odoo.com/'>Odoo</a>"
})
footer = tools.append_content_to_html(footer, signature_company, plaintext=False, container_tag='div')

View File

@ -1049,6 +1049,22 @@ class mrp_production(osv.osv):
return "make_to_order"
return "make_to_stock"
def _create_previous_move(self, cr, uid, move_id, product, source_location_id, dest_location_id, context=None):
'''
When the routing gives a different location than the raw material location of the production order,
we should create an extra move from the raw material location to the location of the routing, which
precedes the consumption line (chained)
'''
stock_move = self.pool.get('stock.move')
move = stock_move.copy(cr, uid, move_id, default = {
'location_id': source_location_id,
'location_dest_id': dest_location_id,
'procure_method': self._get_raw_material_procure_method(cr, uid, product, context=context),
'raw_material_production_id': False,
'move_dest_id': move_id,
}, context=context)
return move
def _make_consume_line_from_data(self, cr, uid, production, product, uom_id, qty, uos_id, uos_qty, context=None):
stock_move = self.pool.get('stock.move')
# Internal shipment is created for Stockable and Consumer Products
@ -1056,12 +1072,13 @@ class mrp_production(osv.osv):
return False
# Take routing location as a Source Location.
source_location_id = production.location_src_id.id
if production.routing_id and production.routing_id.location_id:
source_location_id = production.routing_id.location_id.id
prod_location_id = source_location_id
prev_move= False
if production.bom_id.routing_id and production.bom_id.routing_id.location_id and production.bom_id.routing_id.location_id.id != source_location_id:
source_location_id = production.bom_id.routing_id.location_id.id
prev_move = True
destination_location_id = production.product_id.property_stock_production.id
if not source_location_id:
source_location_id = production.location_src_id.id
move_id = stock_move.create(cr, uid, {
'name': production.name,
'date': production.date_planned,
@ -1073,12 +1090,15 @@ class mrp_production(osv.osv):
'location_id': source_location_id,
'location_dest_id': destination_location_id,
'company_id': production.company_id.id,
'procure_method': self._get_raw_material_procure_method(cr, uid, product, context=context),
'procure_method': prev_move and 'make_to_stock' or self._get_raw_material_procure_method(cr, uid, product, context=context), #Make_to_stock avoids creating procurement
'raw_material_production_id': production.id,
#this saves us a browse in create()
'price_unit': product.standard_price,
'origin': production.name,
})
}, context=context)
if prev_move:
prev_move = self._create_previous_move(cr, uid, move_id, product, prod_location_id, source_location_id, context=context)
return move_id
def _make_production_consume_line(self, cr, uid, line, context=None):

View File

@ -547,29 +547,21 @@
</xpath>
</field>
</record>
<record id="product_template_form_view_inherit" model="ir.ui.view">
<field name="name">product.product.form.view.inherited</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<record id="view_mrp_product_template_form_inherited" model="ir.ui.view">
<field name="name">product.form.mrp.inherited</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="stock.view_template_property_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='lot']" position="inside">
<field name="track_production" groups="stock.group_production_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
</xpath>
<group name="sale_condition" position="inside">
<label for="produce_delay" attrs="{'invisible':[('type','=','service')]}"/>
<div attrs="{'invisible':[('type','=','service')]}">
<field name="produce_delay" class="oe_inline"/> days
</div>
</group>
</field>
</record>
<record id="view_mrp_product_form_inherited" model="ir.ui.view">
<field name="name">product.form.mrp.inherited</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.product_form_view_procurement_button"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='lot']" position="inside">
<field name="track_production" groups="stock.group_production_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
</xpath>
</field>
</record>
@ -1052,6 +1044,7 @@
<field name="res_model">mrp.production</field>
<field name="view_id" ref="mrp_production_tree_view"/>
</record>
<record model="ir.ui.view" id="product_template_form_view_bom_button">
<field name="name">product.template.procurement</field>
<field name="model">product.template</field>
@ -1065,6 +1058,7 @@
</div>
</field>
</record>
<record model="ir.ui.view" id="product_product_form_view_bom_button">
<field name="name">product.product.procurement</field>
<field name="model">product.product</field>
@ -1075,6 +1069,24 @@
groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask">
<field string="Bill of Materials" name="bom_count" widget="statinfo" />
</button>
<button class="oe_inline oe_stat_button" name="action_view_mos" type="object"
groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-list-alt">
<field string="Manufacturing" name="mo_count" widget="statinfo" />
</button>
</div>
</field>
</record>
<record model="ir.ui.view" id="product_normal_form_view_bom_button">
<field name="name">product.product.mrp.button</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<div name="buttons" position="inside">
<button class="oe_inline oe_stat_button" name="action_view_bom" type="object"
groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask">
<field string="Bill of Materials" name="bom_count" widget="statinfo" />
</button>
<button class="oe_inline oe_stat_button" name="%(act_product_mrp_production)d" type="action"
groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-list-alt">
<field string="Manufacturing" name="mo_count" widget="statinfo" />

View File

@ -47,10 +47,20 @@ class product_template(osv.osv):
'produce_delay': fields.float('Manufacturing Lead Time', help="Average delay in days to produce this product. In the case of multi-level BOM, the manufacturing lead times of the components will be added."),
'track_production': fields.boolean('Track Manufacturing Lots', help="Forces to specify a Serial Number for all moves containing this product and generated by a Manufacturing Order"),
}
_defaults = {
"produce_delay": 1,
'produce_delay': 1,
}
def action_view_mos(self, cr, uid, ids, context=None):
products = self._get_products(cr, uid, ids, context=context)
result = self._get_act_window_dict(cr, uid, 'mrp.act_product_mrp_production', context=context)
result['domain'] = "[('product_id','in',[" + ','.join(map(str, products)) + "])]"
result['context'] = "{}"
return result
class product_product(osv.osv):
_inherit = "product.product"
@ -64,7 +74,15 @@ class product_product(osv.osv):
_columns = {
'mo_count': fields.function(_bom_orders_count, string='# Manufacturing Orders', type='integer'),
}
def action_view_bom(self, cr, uid, ids, context=None):
tmpl_obj = self.pool.get("product.template")
products = set()
for product in self.browse(cr, uid, ids, context=context):
products.add(product.product_tmpl_id.id)
result = tmpl_obj._get_act_window_dict(cr, uid, 'mrp.product_open_bom', context=context)
result['context'] = "{}"
result['domain'] = "[('product_tmpl_id','in',[" + ','.join(map(str, list(products))) + "])]"
return result
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -87,7 +87,7 @@
<field name="res_model">report.mrp.inout</field>
<field name="view_type">form</field>
<field name="view_mode">graph,tree</field>
<field name="help">Weekly Stock Value Variation enables you to track the stock value evolution linked to manufacturing activities, receptions of products and delivery orders.</field>
<field name="help">Weekly Stock Value Variation enables you to track the stock value evolution linked to manufacturing activities, receipts of products and delivery orders.</field>
</record>
</data>

View File

@ -85,6 +85,8 @@ class StockMove(osv.osv):
'product_uos_qty': line['product_uos_qty'],
'state': state,
'name': line['name'],
'procurement_id': move.procurement_id.id,
'split_from': move.id, #Needed in order to keep purchase connection, but will be removed by unlink
}
mid = move_obj.copy(cr, uid, move.id, default=valdef)
to_explode_again_ids.append(mid)

View File

@ -4,4 +4,4 @@ access_mail_mail_portal,mail.mail.portal,mail.model_mail_mail,base.group_portal,
access_mail_notification_portal,mail.notification.portal,mail.model_mail_notification,base.group_portal,1,1,1,0
access_mail_followers_portal,mail.followers.portal,mail.model_mail_followers,base.group_portal,1,1,0,0
access_ir_attachment_group_portal,ir.attachment group_portal,base.model_ir_attachment,base.group_portal,1,0,1,0
access_ir_ui_menu_group_portal","ir_ui_menu group_portal","base.model_ir_ui_menu",base.group_portal,1,0,0,0
"access_ir_ui_menu_group_portal","ir_ui_menu group_portal","base.model_ir_ui_menu",base.group_portal,1,0,0,0

Can't render this file because it contains an unexpected character in line 7 and column 31.

View File

@ -195,7 +195,7 @@ class procurement_order(osv.osv):
def run(self, cr, uid, ids, context=None):
for procurement_id in ids:
#we intentionnaly do the browse under the for loop to avoid caching all ids which would be ressource greedy
#we intentionnaly do the browse under the for loop to avoid caching all ids which would be resource greedy
#and useless as we'll make a refresh later that will invalidate all the cache (and thus the next iteration
#will fetch all the ids again)
procurement = self.browse(cr, uid, procurement_id, context=context)

View File

@ -492,7 +492,7 @@ class product_template(osv.osv):
help="A precise description of the Product, used only for internal information purposes."),
'description_purchase': fields.text('Purchase Description',translate=True,
help="A description of the Product that you want to communicate to your suppliers. "
"This description will be copied to every Purchase Order, Reception and Supplier Invoice/Refund."),
"This description will be copied to every Purchase Order, Receipt and Supplier Invoice/Refund."),
'description_sale': fields.text('Sale Description',translate=True,
help="A description of the Product that you want to communicate to your customers. "
"This description will be copied to every Sale Order, Delivery Order and Customer Invoice/Refund"),
@ -550,7 +550,7 @@ class product_template(osv.osv):
"the picking order and is mainly used if you use the EDI module."),
'seller_ids': fields.one2many('product.supplierinfo', 'product_tmpl_id', 'Supplier'),
'seller_delay': fields.related('seller_ids','delay', type='integer', string='Supplier Lead Time',
help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."),
help="This is the average delay in days between the purchase order confirmation and the receipts for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."),
'seller_qty': fields.related('seller_ids','qty', type='float', string='Supplier Quantity',
help="This is minimum quantity to purchase from Main Supplier."),
'seller_id': fields.related('seller_ids','name', type='many2one', relation='res.partner', string='Main Supplier',
@ -1162,7 +1162,7 @@ class product_supplierinfo(osv.osv):
'min_qty': fields.float('Minimal Quantity', required=True, help="The minimal quantity to purchase to this supplier, expressed in the supplier Product Unit of Measure if not empty, in the default unit of measure of the product otherwise."),
'qty': fields.function(_calc_qty, store=True, type='float', string='Quantity', multi="qty", help="This is a quantity which is converted into Default Unit of Measure."),
'product_tmpl_id' : fields.many2one('product.template', 'Product Template', required=True, ondelete='cascade', select=True, oldname='product_id'),
'delay' : fields.integer('Delivery Lead Time', required=True, help="Lead time in days between the confirmation of the purchase order and the reception of the products in your warehouse. Used by the scheduler for automatic computation of the purchase order planning."),
'delay' : fields.integer('Delivery Lead Time', required=True, help="Lead time in days between the confirmation of the purchase order and the receipt of the products in your warehouse. Used by the scheduler for automatic computation of the purchase order planning."),
'pricelist_ids': fields.one2many('pricelist.partnerinfo', 'suppinfo_id', 'Supplier Pricelist', copy=True),
'company_id':fields.many2one('res.company','Company',select=1),
}

View File

@ -44,6 +44,7 @@
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.common.form</field>
<field name="model">product.template</field>
<field name="mode">primary</field>
<field name="arch" type="xml">
<form string="Product">
<field name="is_product_variant" invisible="1"/>
@ -110,7 +111,7 @@
</group>
</group>
</page>
<page string="Sales" attrs="{'invisible':[('sale_ok','=',False)]}">
<page string="Sales" attrs="{'invisible':[('sale_ok','=',False)]}" name="sales">
<group name="sale">
<group name="sale_condition" string="Sale Conditions" colspan="3">
<label for="warranty"/>
@ -269,7 +270,7 @@
<field name="name"/>
</a>
</h4>
<a name="%(product.product_variant_action)d" type="action">
<a name="%(product.product_variant_action)d" type="action" t-if="record.product_variant_count.raw_value&gt;1" >
<t t-esc="record.product_variant_count.value"/> Variants
</a>
<div name="tags"/>
@ -318,7 +319,6 @@
<field name="state" invisible="1"/>
<field name="product_tmpl_id" invisible="1"/>
</tree>
</field>
</record>

View File

@ -257,7 +257,7 @@
<li>Request for Quotation</li>
<li>Purchase order</li>
<li>Invoicing & Invoice control</li>
<li>Incoming order (Complete/partial reception)</li>
<li>Incoming order (Complete/partial receipt)</li>
<li>Purchase requisition</li>
</ul>
</td>
@ -465,7 +465,7 @@
</ul>
</td>
<td style="width:50%;">
From the sales perspective, you will have a manufacturing order generated based on a request coming from a customer without any manual operation. This integration of those two aplications allows you to perform the following flow in an automated way: Sale order > Stock level verification > Production of the missing units > Reception in stock of the finished goods > Delivery to the customer.
From the sales perspective, you will have a manufacturing order generated based on a request coming from a customer without any manual operation. This integration of those two aplications allows you to perform the following flow in an automated way: Sale order > Stock level verification > Production of the missing units > Receival in stock of the finished goods > Delivery to the customer.
</td>
</tr>
</table>

View File

@ -25,7 +25,7 @@
'version': '1.1',
'category': 'Purchase Management',
'sequence': 19,
'summary': 'Purchase Orders, Receptions, Supplier Invoices',
'summary': 'Purchase Orders, Receipts, Supplier Invoices',
'description': """
Manage goods requirement by Purchase Orders easily
==================================================
@ -40,7 +40,7 @@ Dashboard / Reports for Purchase Management will include:
* Request for Quotations
* Purchase Orders Waiting Approval
* Monthly Purchases by Category
* Receptions Analysis
* Receipt Analysis
* Purchase Analysis
""",
'author': 'OpenERP SA',

View File

@ -25,7 +25,7 @@
This supplier has no purchase order. Click to create a new RfQ.
</p><p>
The request for quotation is the first step of the purchases flow. Once
converted into a purchase order, you will be able to control the reception
converted into a purchase order, you will be able to control the receipt
of the products and the supplier invoice.
</p>
</field>
@ -60,7 +60,7 @@
Click here to record a supplier invoice.
</p><p>
Supplier invoices can be pre-generated based on purchase
orders or receptions. This allows you to control invoices
orders or receipts. This allows you to control invoices
you receive from your supplier according to the draft
document in OpenERP.
</p>

View File

@ -232,7 +232,7 @@ class purchase_order(osv.osv):
"to 'Confirmed'. Then the supplier must confirm the order to change "
"the status to 'Approved'. When the purchase order is paid and "
"received, the status becomes 'Done'. If a cancel action occurs in "
"the invoice or in the reception of goods, the status becomes "
"the invoice or in the receipt of goods, the status becomes "
"in exception.",
select=True, copy=False),
'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines',
@ -244,7 +244,7 @@ class purchase_order(osv.osv):
'invoice_ids': fields.many2many('account.invoice', 'purchase_invoice_rel', 'purchase_id',
'invoice_id', 'Invoices', copy=False,
help="Invoices generated for a purchase order"),
'picking_ids': fields.function(_get_picking_ids, method=True, type='one2many', relation='stock.picking', string='Picking List', help="This is the list of reception operations that have been generated for this purchase order."),
'picking_ids': fields.function(_get_picking_ids, method=True, type='one2many', relation='stock.picking', string='Picking List', help="This is the list of receipts that have been generated for this purchase order."),
'shipped':fields.boolean('Received', readonly=True, select=True, copy=False,
help="It indicates that a picking has been done"),
'shipped_rate': fields.function(_shipped_rate, string='Received Ratio', type='float'),
@ -255,7 +255,7 @@ class purchase_order(osv.osv):
readonly=True, states={'draft':[('readonly',False)], 'sent':[('readonly',False)]},
help="Based on Purchase Order lines: place individual lines in 'Invoice Control / On Purchase Order lines' from where you can selectively create an invoice.\n" \
"Based on generated invoice: create a draft invoice you can validate later.\n" \
"Based on incoming shipments: let you create an invoice when receptions are validated."
"Based on incoming shipments: let you create an invoice when receipts are validated."
),
'minimum_planned_date':fields.function(_minimum_planned_date, fnct_inv=_set_minimum_planned_date, string='Expected Date', type='date', select=True, help="This is computed as the minimum scheduled date of all purchase order lines' products.",
store = {
@ -678,7 +678,7 @@ class purchase_order(osv.osv):
if pick.state not in ('draft', 'cancel'):
raise osv.except_osv(
_('Unable to cancel the purchase order %s.') % (purchase.name),
_('First cancel all receptions related to this purchase order.'))
_('First cancel all receipts related to this purchase order.'))
self.pool.get('stock.picking') \
.signal_workflow(cr, uid, map(attrgetter('id'), purchase.picking_ids), 'button_cancel')
for inv in purchase.invoice_ids:
@ -1391,15 +1391,23 @@ class product_template(osv.Model):
for template in self.browse(cr, uid, ids, context=context):
res[template.id] = sum([p.purchase_count for p in template.product_variant_ids])
return res
_columns = {
'purchase_ok': fields.boolean('Can be Purchased', help="Specify if the product can be selected in a purchase order line."),
'purchase_count': fields.function(_purchase_count, string='# Purchases', type='integer'),
}
_defaults = {
'purchase_ok': 1,
'route_ids': _get_buy_route,
}
def action_view_purchases(self, cr, uid, ids, context=None):
products = self._get_products(cr, uid, ids, context=context)
result = self._get_act_window_dict(cr, uid, 'purchase.action_purchase_line_product_tree', context=context)
result['domain'] = "[('product_id','in',[" + ','.join(map(str, products)) + "])]"
return result
class product_product(osv.Model):
_name = 'product.product'
_inherit = 'product.product'
@ -1430,7 +1438,7 @@ class mail_compose_message(osv.Model):
class account_invoice(osv.Model):
""" Override account_invoice to add Chatter messages on the related purchase
orders, logging the invoice reception or payment. """
orders, logging the invoice receipt or payment. """
_inherit = 'account.invoice'
def invoice_validate(self, cr, uid, ids, context=None):
@ -1475,9 +1483,5 @@ class account_invoice_line(osv.Model):
readonly=True),
}
class product_template(osv.osv):
_inherit = "product.template"
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -87,7 +87,7 @@
<!--Inventory control-->
<menuitem id="menu_procurement_management_inventory" name="Incoming Products"
parent="base.menu_purchase_root" sequence="4"/>
<menuitem action="stock.action_reception_picking_move" id="menu_action_picking_tree_in_move"
<menuitem action="stock.action_receipt_picking_move" id="menu_action_picking_tree_in_move"
parent="menu_procurement_management_inventory" sequence="11"/>
<!--Invoice control-->
@ -109,7 +109,7 @@
</p><p>
Use this menu to control the invoices to be received from your
supplier. OpenERP generates draft invoices from your purchase
orders or receptions, according to your settings.
orders or receipts, according to your settings.
</p><p>
Once you receive a supplier invoice, you can match it with the
draft invoice and validate it.
@ -620,12 +620,6 @@
<field name="model">product.template</field>
<field name="inherit_id" ref="account.product_template_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" name="%(action_purchase_line_product_tree)d" type="action"
groups="purchase.group_purchase_user" icon="fa-shopping-cart">
<field string="Purchases" name="purchase_count" widget="statinfo" />
</button>
</xpath>
<field name="property_account_expense" position="replace" >
<field name="property_account_expense" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]" attrs="{'readonly':[('purchase_ok','=',0)]}" groups="account.group_account_user"/>
</field>
@ -634,5 +628,33 @@
</field>
</field>
</record>
<record id="view_product_template_purchase_buttons_from" model="ir.ui.view">
<field name="name">product.template.purchase.button.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" name="action_view_purchases" type="object"
groups="purchase.group_purchase_user" icon="fa-shopping-cart">
<field string="Purchases" name="purchase_count" widget="statinfo"/>
</button>
</xpath>
</field>
</record>
<record id="view_product_normal_purchase_buttons_from" model="ir.ui.view">
<field name="name">product.product.purchase.button.inherit</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" name="%(action_purchase_line_product_tree)d" type="action"
groups="purchase.group_purchase_user" icon="fa-shopping-cart">
<field string="Purchases" name="purchase_count" widget="statinfo"/>
</button>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -46,8 +46,8 @@ class stock_move(osv.osv):
return res
def copy(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
default = default or {}
context = context or {}
if not default.get('split_from'):
#we don't want to propagate the link to the purchase order line except in case of move split
default['purchase_line_id'] = False
@ -102,7 +102,7 @@ class stock_picking(osv.osv):
_columns = {
'reception_to_invoice': fields.function(_get_to_invoice, type='boolean', string='Invoiceable on incoming shipment?',
help='Does the picking contains some moves related to a purchase order invoiceable on the reception?',
help='Does the picking contains some moves related to a purchase order invoiceable on the receipt?',
store={
'stock.move': (_get_picking_to_recompute, ['purchase_line_id', 'picking_id'], 10),
}),

View File

@ -72,10 +72,10 @@
<p class="oe_view_nocontent_create">
Click to create a new incoming shipment.
</p><p>
Here you can track all the product receptions of purchase
Here you can track all the product receipts of purchase
orders where the invoicing is "Based on Incoming Shipments",
and for which you have not received a supplier invoice yet.
You can generate a supplier invoice based on those receptions.
You can generate a supplier invoice based on those receipts.
</p>
</field>
</record>

View File

@ -718,7 +718,7 @@ class sale_order(osv.osv):
procurement_obj.check(cr, uid, [x.id for x in line.procurement_ids if x.state not in ['cancel', 'done']])
line.refresh()
#run again procurement that are in exception in order to trigger another move
proc_ids += [x.id for x in line.procurement_ids if x.state == 'exception']
proc_ids += [x.id for x in line.procurement_ids if x.state in ('exception', 'cancel')]
elif sale_line_obj.need_procurement(cr, uid, [line.id], context=context):
if (line.state == 'done') or not line.product_id:
continue
@ -741,49 +741,7 @@ class sale_order(osv.osv):
order.write(val)
return True
# if mode == 'finished':
# returns True if all lines are done, False otherwise
# if mode == 'canceled':
# returns True if there is at least one canceled line, False otherwise
def test_state(self, cr, uid, ids, mode, *args):
assert mode in ('finished', 'canceled'), _("invalid mode for test_state")
finished = True
canceled = False
write_done_ids = []
write_cancel_ids = []
for order in self.browse(cr, uid, ids, context={}):
#TODO: Need to rethink what happens when cancelling
for line in order.order_line:
states = [x.state for x in line.procurement_ids]
cancel = states and all([x == 'cancel' for x in states])
doneorcancel = all([x in ('done', 'cancel') for x in states])
if cancel:
canceled = True
if line.state != 'exception':
write_cancel_ids.append(line.id)
if not doneorcancel:
finished = False
if doneorcancel and not cancel:
write_done_ids.append(line.id)
if write_done_ids:
self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'})
if write_cancel_ids:
self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'exception'})
if mode == 'finished':
return finished
elif mode == 'canceled':
return canceled
def procurement_lines_get(self, cr, uid, ids, *args):
res = []
for order in self.browse(cr, uid, ids, context={}):
for line in order.order_line:
res += [x.id for x in line.procurement_ids]
return res
def onchange_fiscal_position(self, cr, uid, ids, fiscal_position, order_lines, context=None):
'''Update taxes of order lines for each line where a product is defined
@ -828,6 +786,20 @@ class sale_order(osv.osv):
order_line.append(line)
return {'value': {'order_line': order_line}}
def test_procurements_done(self, cr, uid, ids, context=None):
for sale in self.browse(cr, uid, ids, context=context):
for line in sale.order_line:
if not all([x.state == 'done' for x in line.procurement_ids]):
return False
return True
def test_procurements_except(self, cr, uid, ids, context=None):
for sale in self.browse(cr, uid, ids, context=context):
for line in sale.order_line:
if any([x.state == 'cancel' for x in line.procurement_ids]):
return True
return False
# TODO add a field price_unit_uos
# - update it on change product and unit price
@ -924,6 +896,8 @@ class sale_order_line(osv.osv):
'delay': 0.0,
}
def _get_line_qty(self, cr, uid, line, context=None):
if line.product_uos:
return line.product_uos_qty or 0.0
@ -1268,6 +1242,20 @@ class procurement_order(osv.osv):
'sale_line_id': fields.many2one('sale.order.line', string='Sale Order Line'),
}
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
res = super(procurement_order, self).write(cr, uid, ids, vals, context=context)
from openerp import workflow
if vals.get('state') in ['done', 'cancel', 'exception']:
for proc in self.browse(cr, uid, ids, context=context):
if proc.sale_line_id and proc.sale_line_id.order_id and proc.move_ids:
order_id = proc.sale_line_id.order_id.id
if self.pool.get('sale.order').test_procurements_done(cr, uid, [order_id], context=context):
workflow.trg_validate(uid, 'sale.order', order_id, 'ship_end', cr)
if self.pool.get('sale.order').test_procurements_except(cr, uid, [order_id], context=context):
workflow.trg_validate(uid, 'sale.order', order_id, 'ship_except', cr)
return res
class product_product(osv.Model):
_inherit = 'product.product'
@ -1278,10 +1266,36 @@ class product_product(osv.Model):
product_id: SaleOrderLine.search_count(cr,uid, [('product_id', '=', product_id)], context=context)
for product_id in ids
}
_columns = {
'sales_count': fields.function(_sales_count, string='# Sales', type='integer'),
}
class product_template(osv.Model):
_inherit = 'product.template'
def _sales_count(self, cr, uid, ids, field_name, arg, context=None):
res = dict.fromkeys(ids, 0)
for template in self.browse(cr, uid, ids, context=context):
res[template.id] = sum([p.sales_count for p in template.product_variant_ids])
return res
def action_view_sales(self, cr, uid, ids, context=None):
act_obj = self.pool.get('ir.actions.act_window')
mod_obj = self.pool.get('ir.model.data')
product_ids = []
for template in self.browse(cr, uid, ids, context=context):
product_ids += [x.id for x in template.product_variant_ids]
result = mod_obj.xmlid_to_res_id(cr, uid, 'sale.action_order_line_product_tree',raise_if_not_found=True)
result = act_obj.read(cr, uid, [result], context=context)[0]
result['domain'] = "[('product_id','in',[" + ','.join(map(str, product_ids)) + "])]"
return result
_columns = {
'sales_count': fields.function(_sales_count, string='# Sales', type='integer'),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -508,6 +508,21 @@
</field>
</record>
<record model="ir.ui.view" id="product_template_form_view_sale_order_button">
<field name="name">product.template.sale.order.button</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" name="action_view_sales"
type="object" groups="base.group_sale_salesman" icon="fa-strikethrough">
<field string="Sales" name="sales_count" widget="statinfo" />
</button>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="view_company_inherit_form2">
<field name="name">res.company.form.inherit</field>
<field name="inherit_id" ref="base.view_company_form"/>

View File

@ -262,9 +262,7 @@
<record id="trans_ship_ship_end" model="workflow.transition">
<field name="act_from" ref="act_ship"/>
<field name="act_to" ref="act_ship_end"/>
<field name="trigger_model">procurement.order</field>
<field name="trigger_expr_id">procurement_lines_get()</field>
<field name="condition">test_state('finished')</field>
<field name="signal">ship_end</field>
</record>
<record id="trans_ship_ship_except" model="workflow.transition">

View File

@ -350,15 +350,6 @@ class sale_order_line(osv.osv):
class stock_move(osv.osv):
_inherit = 'stock.move'
def action_cancel(self, cr, uid, ids, context=None):
sale_ids = []
for move in self.browse(cr, uid, ids, context=context):
if move.procurement_id and move.procurement_id.sale_line_id:
sale_ids.append(move.procurement_id.sale_line_id.order_id.id)
if sale_ids:
self.pool.get('sale.order').signal_workflow(cr, uid, sale_ids, 'ship_except')
return super(stock_move, self).action_cancel(cr, uid, ids, context=context)
def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None):
invoice_line_id = super(stock_move, self)._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context)
if move.procurement_id and move.procurement_id.sale_line_id:

View File

@ -27,7 +27,7 @@ To satisfy the need for a counterpart to each stock movement, the software suppo
* Partner locations (suppliers and customers),
* Virtual locations as counterparts for production, inventory and scrap.
Partner locations represent your customers' and suppliers' stocks. To reconcile them with your accounts, these stores play the role of third-party accounts. Reception from a supplier can be shown by the movement of goods from a partner location to a physical location in your own company. As you see, supplier locations usually show negative stocks and customer locations usually show positive stocks.
Partner locations represent your customers' and suppliers' stocks. To reconcile them with your accounts, these stores play the role of third-party accounts. Receipt from a supplier can be shown by the movement of goods from a partner location to a physical location in your own company. As you see, supplier locations usually show negative stocks and customer locations usually show positive stocks.
Virtual locations as counterparts for production are used in manufacturing operations. Manufacturing is characterized by the consumption of raw materials and the production of finished products. Virtual locations are used for the counterparts of these two operations.

View File

@ -222,27 +222,18 @@ class procurement_order(osv.osv):
'''
if procurement.rule_id and procurement.rule_id.action == 'move':
uom_obj = self.pool.get('product.uom')
done_test_list = []
done_cancel_test_list = []
qty_done = 0
for move in procurement.move_ids:
done_test_list.append(move.state == 'done')
done_cancel_test_list.append(move.state in ('done', 'cancel'))
qty_done += move.product_qty if move.state == 'done' else 0
qty_done = uom_obj._compute_qty(cr, uid, procurement.product_id.uom_id.id, qty_done, procurement.product_uom.id)
at_least_one_done = any(done_test_list)
cancel_test_list = [x.state == 'cancel' for x in procurement.move_ids]
done_cancel_test_list = [x.state in ('done', 'cancel') for x in procurement.move_ids]
at_least_one_cancel = any(cancel_test_list)
all_done_or_cancel = all(done_cancel_test_list)
all_cancel = all(cancel_test_list)
if not all_done_or_cancel:
return False
elif all_done_or_cancel and procurement.product_qty == qty_done:
elif all_done_or_cancel and not all_cancel:
return True
elif at_least_one_done:
#some move cancelled and some validated
self.message_post(cr, uid, [procurement.id], body=_('Some stock moves have been cancelled for this procurement. Run the procurement again to trigger a move for the remaining quantity or change the procurement quantity to finish it directly'), context=context)
else:
#all move are cancelled
elif all_cancel:
self.message_post(cr, uid, [procurement.id], body=_('All stock moves have been cancelled for this procurement.'), context=context)
self.write(cr, uid, [procurement.id], {'state': 'exception'}, context=context)
self.write(cr, uid, [procurement.id], {'state': 'cancel'}, context=context)
return False
return super(procurement_order, self)._check(cr, uid, procurement, context)

View File

@ -175,7 +175,7 @@ class product_product(osv.osv):
return res
_columns = {
'reception_count': fields.function(_stock_move_count, string="Reception", type='integer', multi='pickings'),
'reception_count': fields.function(_stock_move_count, string="Receipt", type='integer', multi='pickings'),
'delivery_count': fields.function(_stock_move_count, string="Delivery", type='integer', multi='pickings'),
'qty_available_text': fields.function(_product_available_text, type='char'),
'qty_available': fields.function(_product_available, multi='qty_available',
@ -244,7 +244,7 @@ class product_product(osv.osv):
if fields:
if location_info.usage == 'supplier':
if fields.get('virtual_available'):
res['fields']['virtual_available']['string'] = _('Future Receptions')
res['fields']['virtual_available']['string'] = _('Future Receipts')
if fields.get('qty_available'):
res['fields']['qty_available']['string'] = _('Received Qty')
@ -323,8 +323,18 @@ class product_template(osv.osv):
res.append(('product_variant_ids', 'in', ids))
return res
def _product_available_text(self, cr, uid, ids, field_names=None, arg=False, context=None):
res = {}
for product in self.browse(cr, uid, ids, context=context):
res[product.id] = str(product.qty_available) + _(" On Hand")
return res
_columns = {
'type': fields.selection([('product', 'Stockable Product'), ('consu', 'Consumable'), ('service', 'Service')], 'Product Type', required=True, help="Consumable: Will not imply stock management for this product. \nStockable product: Will imply stock management for this product."),
'qty_available_text': fields.function(_product_available_text, type='char'),
'property_stock_procurement': fields.property(
type='many2one',
relation='stock.location',
@ -382,12 +392,48 @@ class product_template(osv.osv):
product_route_ids |= set([r.id for r in product.route_ids])
product_route_ids |= set([r.id for r in product.categ_id.total_route_ids])
route_ids = route_obj.search(cr, uid, ['|', ('id', 'in', list(product_route_ids)), ('warehouse_selectable', '=', True)], context=context)
result = mod_obj.get_object_reference(cr, uid, 'stock', 'action_routes_form')
id = result and result[1] or False
result = act_obj.read(cr, uid, [id], context=context)[0]
result = mod_obj.xmlid_to_res_id(cr, uid, 'stock.action_routes_form', raise_if_not_found=True)
result = act_obj.read(cr, uid, [result], context=context)[0]
result['domain'] = "[('id','in',[" + ','.join(map(str, route_ids)) + "])]"
return result
def _get_products(self, cr, uid, ids, context=None):
products = []
for prodtmpl in self.browse(cr, uid, ids, context=None):
products += [x.id for x in prodtmpl.product_variant_ids]
return products
def _get_act_window_dict(self, cr, uid, name, context=None):
mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
result = mod_obj.xmlid_to_res_id(cr, uid, name, raise_if_not_found=True)
result = act_obj.read(cr, uid, [result], context=context)[0]
return result
def action_open_quants(self, cr, uid, ids, context=None):
products = self._get_products(cr, uid, ids, context=context)
result = self._get_act_window_dict(cr, uid, 'stock.product_open_quants', context=context)
result['domain'] = "[('product_id','in',[" + ','.join(map(str, products)) + "])]"
result['context'] = "{'search_default_locationgroup': 1, 'search_default_internal_loc': 1}"
return result
def action_view_orderpoints(self, cr, uid, ids, context=None):
products = self._get_products(cr, uid, ids, context=context)
result = self._get_act_window_dict(cr, uid, 'stock.product_open_orderpoint', context=context)
result['domain'] = "[('product_id','in',[" + ','.join(map(str, products)) + "])]"
result['context'] = "{}"
return result
def action_view_stock_moves(self, cr, uid, ids, context=None):
products = self._get_products(cr, uid, ids, context=context)
result = self._get_act_window_dict(cr, uid, 'stock.act_product_stock_move_open', context=context)
result['domain'] = "[('product_id','in',[" + ','.join(map(str, products)) + "])]"
result['context'] = "{}"
return result
class product_removal_strategy(osv.osv):
_name = 'product.removal'
_description = 'Removal Strategy'

View File

@ -16,21 +16,36 @@
</field>
</record>
<record id="view_stock_product_template_tree" model="ir.ui.view">
<field name="name">product.template.stock.tree.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_tree_view"/>
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="colors">{'red':virtual_available&lt;0, 'blue':virtual_available&gt;=0 and state in ('draft', 'end', 'obsolete'), 'black':virtual_available&gt;=0 and state not in ('draft', 'end', 'obsolete')}</attribute>
</tree>
<tree position="inside">
<field name="virtual_available" invisible="1"/>
</tree>
</field>
</record>
<record id="action_receive_move" model="ir.actions.act_window">
<field name="name">Receptions</field>
<field name="name">Receipts</field>
<field name="res_model">stock.move</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain" eval="[('picking_id.picking_type_id.code','=','incoming'), ('location_id.usage','!=','internal'), ('location_dest_id.usage', '=', 'internal')]"/>
<field name="view_id" ref="view_move_tree_reception_picking"/>
<field name="view_id" ref="view_move_tree_receipt_picking"/>
<field name="context" eval="'{\'search_default_product_id\': [active_id]}'"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to register a reception for this product.
Click to register a receipt for this product.
</p><p>
Here you will find the history of all receptions related to
this product, as well as all future receptions you are waiting
Here you will find the history of all receipts related to
this product, as well as all future receipts you are waiting
from your suppliers.
</p>
</field>
@ -42,7 +57,7 @@
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_move_tree_reception_picking"/>
<field name="view_id" ref="view_move_tree_receipt_picking"/>
<field name="domain" eval="[('picking_type_id.code','=','outgoing'), ('location_id.usage','=','internal'), ('location_dest_id.usage', '!=', 'internal')]"/>
<field name="context" eval="'{\'search_default_product_id\': [active_id]}'"/>
<field name="help" type="html">
@ -111,6 +126,13 @@
<field name="loc_case"/>
</group>
</group>
<group name="status" position="before">
<group name="lot" groups="stock.group_production_lot" string="Lots" attrs="{'invisible':[('type','=','service')]}">
<field name="track_all" groups="stock.group_production_lot"/>
<field name="track_incoming" groups="stock.group_production_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
<field name="track_outgoing" groups="stock.group_production_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
</group>
</group>
<group name="weight" position="before">
<group name="store" groups="stock.group_locations" string="Counter-Part Locations Properties">
<field name="property_stock_procurement" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','procurement')]"/>
@ -121,9 +143,6 @@
<field name="product_manager" position="attributes">
<attribute name="context">{'default_groups_ref': ['base.group_user', 'base.group_sale_manager', 'stock.group_stock_manager']}</attribute>
</field>
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" name="action_view_routes" string="Routes" type="object" icon="fa-cogs"/>
</xpath>
<group name="procurement_uom" position="after" >
<group string="Supply Chain Information" attrs="{'invisible': [('type', '=', 'service')]}" groups="base.group_user">
<field name="route_ids" widget="many2many_checkboxes"/>
@ -132,6 +151,8 @@
</field>
</record>
<record model="ir.ui.view" id="product_template_kanban_stock_view">
<field name="name">Product Template Kanban Stock</field>
<field name="model">product.template</field>
@ -172,10 +193,10 @@
</field>
<xpath expr="//div[@name='tags']" position="inside">
<a name="%(action_receive_move)d" type="action" t-if="record.reception_count.raw_value&gt;1">
<t t-esc="record.reception_count.value"/> Receptions
<t t-esc="record.reception_count.value"/> Receipts
</a>
<a name="%(action_receive_move)d" type="action" t-if="record.reception_count.raw_value==1">
<t t-esc="record.reception_count.value"/> Reception
<t t-esc="record.reception_count.value"/> Receipt
</a>
<a name="%(action_deliver_move)d" type="action" t-if="record.delivery_count.raw_value&gt;1">
<t t-esc="record.delivery_count.value"/> Deliveries
@ -192,13 +213,6 @@
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<group name="status" position="before">
<group name="lot" groups="stock.group_production_lot" string="Lots">
<field name="track_all" groups="stock.group_production_lot"/>
<field name="track_incoming" groups="stock.group_production_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
<field name="track_outgoing" groups="stock.group_production_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
</group>
</group>
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_stat_button"
name="%(product_open_quants)d"
@ -209,6 +223,27 @@
<button class="oe_inline oe_stat_button" string="Moves" name= "%(act_product_stock_move_open)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_stock_user" icon="fa-arrows-v"/>
<button class="oe_inline oe_stat_button" name="%(product_open_orderpoint)d" type="action"
attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-refresh" string="Reordering Rules"/>
<button class="oe_inline oe_stat_button" name="action_view_routes" string="Routes" type="object" icon="fa-cogs" attrs="{'invisible':[('type', '=', 'service')]}" />
</xpath>
</field>
</record>
<record model="ir.ui.view" id="product_template_form_view_procurement_button">
<field name="name">product.template_procurement</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_stat_button"
name="action_open_quants"
icon="fa-building-o"
type="object" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_locations">
<div><field name="qty_available_text"/></div>
</button>
<button class="oe_inline oe_stat_button" string="Moves" name= "action_view_stock_moves" type="object" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_stock_user" icon="fa-arrows-v"/>
<button class="oe_inline oe_stat_button" name="action_view_orderpoints" type="object"
attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-refresh" string="Reordering Rules"/>
<button class="oe_inline oe_stat_button" name="action_view_routes" string="Routes" type="object" icon="fa-cogs" attrs="{'invisible':[('type', '=', 'service')]}" />
</xpath>
</field>
</record>

View File

@ -1731,6 +1731,7 @@ class stock_move(osv.osv):
'date_expected': fields.datetime.now,
'procure_method': 'make_to_stock',
'propagate': True,
'partially_available': False,
}
def _check_uom(self, cr, uid, ids, context=None):
@ -2137,6 +2138,7 @@ class stock_move(osv.osv):
"""
procurement_obj = self.pool.get('procurement.order')
context = context or {}
procs_to_check = []
for move in self.browse(cr, uid, ids, context=context):
if move.state == 'done':
raise osv.except_osv(_('Operation Forbidden!'),
@ -2147,21 +2149,21 @@ class stock_move(osv.osv):
if move.propagate:
procurement_ids = procurement_obj.search(cr, uid, [('move_dest_id', '=', move.id)], context=context)
procurement_obj.cancel(cr, uid, procurement_ids, context=context)
elif move.move_dest_id:
#cancel chained moves
if move.propagate:
self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)
# If we have a long chain of moves to be cancelled, it is easier for the user to handle
# only the last procurement which will go into exception, instead of all procurements
# along the chain going into exception. We need to check if there are no split moves not cancelled however
if move.procurement_id:
proc = move.procurement_id
if all([x.state == 'cancel' for x in proc.move_ids if x.id != move.id]):
procurement_obj.write(cr, uid, [proc.id], {'state': 'cancel'})
elif move.move_dest_id.state == 'waiting':
self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
return self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
else:
if move.move_dest_id:
if move.propagate:
self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)
elif move.move_dest_id.state == 'waiting':
#If waiting, the chain will be broken and we are not sure if we can still wait for it (=> could take from stock instead)
self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
if move.procurement_id:
# Does the same as procurement check, only eliminating a refresh
procs_to_check.append(move.procurement_id.id)
res = self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
if procs_to_check:
procurement_obj.check(cr, uid, procs_to_check, context=context)
return res
def _check_package_from_moves(self, cr, uid, ids, context=None):
pack_obj = self.pool.get("stock.quant.package")
@ -2379,6 +2381,7 @@ class stock_move(osv.osv):
'restrict_lot_id': restrict_lot_id,
'restrict_partner_id': restrict_partner_id,
'split_from': move.id,
'procurement_id': move.procurement_id.id,
'move_dest_id': move.move_dest_id.id,
}
if context.get('source_location_id'):
@ -2767,7 +2770,7 @@ class stock_warehouse(osv.osv):
'in_type_id': fields.many2one('stock.picking.type', 'In Type'),
'int_type_id': fields.many2one('stock.picking.type', 'Internal Type'),
'crossdock_route_id': fields.many2one('stock.location.route', 'Crossdock Route'),
'reception_route_id': fields.many2one('stock.location.route', 'Reception Route'),
'reception_route_id': fields.many2one('stock.location.route', 'Receipt Route'),
'delivery_route_id': fields.many2one('stock.location.route', 'Delivery Route'),
'resupply_from_wh': fields.boolean('Resupply From Other Warehouses'),
'resupply_wh_ids': fields.many2many('stock.warehouse', 'stock_wh_resupply_table', 'supplied_wh_id', 'supplier_wh_id', 'Resupply Warehouses'),
@ -3069,7 +3072,7 @@ class stock_warehouse(osv.osv):
for pull_rule in pull_rules_list:
pull_obj.create(cr, uid, vals=pull_rule, context=context)
#update reception route and rules: unlink the existing rules of the warehouse reception route and recreate it
#update receipt route and rules: unlink the existing rules of the warehouse receipt route and recreate it
pull_obj.unlink(cr, uid, [pu.id for pu in warehouse.reception_route_id.pull_ids], context=context)
push_obj.unlink(cr, uid, [pu.id for pu in warehouse.reception_route_id.push_ids], context=context)
route_name, values = routes_dict[new_reception_step]
@ -3079,7 +3082,7 @@ class stock_warehouse(osv.osv):
for push_rule in push_rules_list:
push_obj.create(cr, uid, vals=push_rule, context=context)
for pull_rule in pull_rules_list:
#all pull rules in reception route are mto, because we don't want to wait for the scheduler to trigger an orderpoint on input location
#all pull rules in receipt route are mto, because we don't want to wait for the scheduler to trigger an orderpoint on input location
pull_rule['procure_method'] = 'make_to_order'
pull_obj.create(cr, uid, vals=pull_rule, context=context)
@ -3133,7 +3136,7 @@ class stock_warehouse(osv.osv):
max_sequence = max_sequence and max_sequence[0]['sequence'] or 0
in_type_id = picking_type_obj.create(cr, uid, vals={
'name': _('Receptions'),
'name': _('Receipts'),
'warehouse_id': warehouse.id,
'code': 'incoming',
'sequence_id': in_seq_id,
@ -3255,9 +3258,9 @@ class stock_warehouse(osv.osv):
customer_loc, supplier_loc = self._get_partner_locations(cr, uid, ids, context=context)
return {
'one_step': (_('Reception in 1 step'), []),
'two_steps': (_('Reception in 2 steps'), [(warehouse.wh_input_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id.id)]),
'three_steps': (_('Reception in 3 steps'), [(warehouse.wh_input_stock_loc_id, warehouse.wh_qc_stock_loc_id, warehouse.int_type_id.id), (warehouse.wh_qc_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id.id)]),
'one_step': (_('Receipt in 1 step'), []),
'two_steps': (_('Receipt in 2 steps'), [(warehouse.wh_input_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id.id)]),
'three_steps': (_('Receipt in 3 steps'), [(warehouse.wh_input_stock_loc_id, warehouse.wh_qc_stock_loc_id, warehouse.int_type_id.id), (warehouse.wh_qc_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id.id)]),
'crossdock': (_('Cross-Dock'), [(warehouse.wh_input_stock_loc_id, warehouse.wh_output_stock_loc_id, warehouse.int_type_id.id), (warehouse.wh_output_stock_loc_id, customer_loc, warehouse.out_type_id.id)]),
'ship_only': (_('Ship Only'), [(warehouse.lot_stock_id, customer_loc, warehouse.out_type_id.id)]),
'pick_ship': (_('Pick + Ship'), [(warehouse.lot_stock_id, warehouse.wh_output_stock_loc_id, warehouse.pick_type_id.id), (warehouse.wh_output_stock_loc_id, customer_loc, warehouse.out_type_id.id)]),
@ -3310,7 +3313,7 @@ class stock_warehouse(osv.osv):
def _check_reception_resupply(self, cr, uid, warehouse, new_location, context=None):
"""
Will check if the resupply routes to this warehouse follow the changes of number of reception steps
Will check if the resupply routes to this warehouse follow the changes of number of receipt steps
"""
#Check routes that are being delivered by this warehouse and change the rule coming from transit location
route_obj = self.pool.get("stock.location.route")
@ -3932,7 +3935,8 @@ class stock_warehouse_orderpoint(osv.osv):
continue
procurement_qty = uom_obj._compute_qty_obj(cr, uid, procurement.product_uom, procurement.product_qty, procurement.product_id.uom_id, context=context)
for move in procurement.move_ids:
if move.state not in ('draft', 'cancel'):
#need to add the moves in draft as they aren't in the virtual quantity + moves that have not been created yet
if move.state not in ('draft'):
#if move is already confirmed, assigned or done, the virtual stock is already taking this into account so it shouldn't be deducted
procurement_qty -= move.product_qty
qty += procurement_qty
@ -3948,7 +3952,6 @@ class stock_warehouse_orderpoint(osv.osv):
for rule in self.browse(cr, uid, ids, context=context):
if rule.product_id.uom_id.category_id.id != rule.product_uom.category_id.id:
return False
return True
def action_view_proc_to_process(self, cr, uid, ids, context=None):

View File

@ -300,58 +300,6 @@
</field>
</record>
<record id="stock_move_tree2" model="ir.ui.view">
<field name="name">Stock Moves</field>
<field name="model">stock.move</field>
<field name="arch" type="xml">
<tree colors="grey:state == 'cancel'" string="Moves" create="false">
<field name="product_id"/>
<field name="product_uom_qty"/>
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
<field name="product_packaging" domain="[('product_id','=',product_id)]" groups="product.group_stock_packaging"/>
<field name="picking_id"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="location_dest_id" groups="stock.group_locations"/>
<field name="create_date" groups="base.group_no_one"/>
<field name="date" string="Date" groups="base.group_no_one"/>
<field name="date_expected" string="Date Expected"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="action3" model="ir.actions.act_window">
<field name="name">Downstream traceability</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">stock.move</field>
<field name="domain">[('id','in',active_ids)]</field>
<field name="view_type">tree</field>
<field eval="stock_move_tree2" name="view_id"/>
</record>
<record id="ir_move_traceability_upstream" model="ir.values">
<field name="key2">tree_but_action</field>
<field name="model">stock.move</field>
<field name="name">Downstream traceability</field>
<field eval="'ir.actions.act_window,'+str(action3)" name="value"/>
</record>
<record id="action5" model="ir.actions.act_window">
<field name="name">Upstream traceability</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">stock.move</field>
<field name="domain">[('id','in',active_ids)]</field>
<field name="view_type">tree</field>
<field eval="stock_move_tree" name="view_id"/>
</record>
<record id="ir_move_traceability_downstream" model="ir.values">
<field name="key2">tree_but_action</field>
<field name="model">stock.move</field>
<field name="name">Upstream traceability</field>
<field eval="'ir.actions.act_window,'+str(action5)" name="value"/>
</record>
<record model="ir.actions.act_window" id="location_open_quants">
<field name="context">{'search_default_productgroup': 1}</field>
<field name="domain">[('location_id', 'child_of', active_ids)]</field>
@ -722,33 +670,6 @@
<notebook>
<page string="Products">
<separator string="Stock Moves" attrs="{'invisible': [('pack_operation_exist', '=', False)]}"/>
<!--
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id,'default_picking_id': active_id}"/>
<group>
<group col="3">
<separator string="Quantities Done" attrs="{'invisible': [('pack_operation_exist', '=', False)]}"/>
</group>
<group col="1">
<field name="pack_operation_exist" invisible="1"/>
<button name="action_pack" string="Create Package" type="object" attrs="{'invisible': ['|',('pack_operation_exist', '=', False),('state', 'not in', ('draft','assigned','partially_available','confirmed'))]}" class="oe_link oe_right oe_inline" groups="product.group_stock_packaging"/>
<button name="do_split" string="Create Draft Backorder" groups="base.group_no_one" type="object" attrs="{'invisible': ['|',('pack_operation_exist', '=', False),('state','not in',('assigned', 'partially_available'))]}" class="oe_link oe_right oe_inline"/>
<button name="recheck_availability" string="Recheck Availability" type="object" attrs="{'invisible': ['|',('pack_operation_exist', '=', False),('state','not in',('assigned', 'partially_available'))]}" groups="stock.group_stock_user" class="oe_link oe_right oe_inline"/>
</group>
</group>
<field name="pack_operation_ids" attrs="{'invisible': [('pack_operation_exist', '=', False)]}" context="{'default_owner_id': owner_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
<tree editable="top">
<field name="location_id"/>
<field name="product_id" on_change='product_id_change(product_id, product_uom_id, product_qty, context)'/>
<field name="product_uom_id" groups="product.group_uom" on_change='on_change_tests(product_id, product_uom_id, product_qty, context)'/>
<field name="lot_id" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}" groups="stock.group_production_lot"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<field name="product_qty" attrs="{'required': [('product_id', '!=', False)]}" on_change='on_change_tests(product_id, product_uom_id, product_qty, context)'/>
<field name="location_dest_id"/>
<field name="result_package_id" groups="stock.group_tracking_lot"/>
</tree>
</field>
-->
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id,'default_picking_id': active_id}"/>
<field name="pack_operation_exist" invisible="1"/>
<field name="note" placeholder="Add an internal note..." class="oe_inline"/>
@ -1234,10 +1155,10 @@
<menuitem action="action_move_form2" id="menu_action_move_form2" parent="menu_traceability" sequence="3" groups="stock.group_locations"/>
<!--
Reception Picking (By Stock Move)
Receipt Picking (By Stock Move)
From stock_partial_move_view
-->
<record id="view_move_tree_reception_picking" model="ir.ui.view">
<record id="view_move_tree_receipt_picking" model="ir.ui.view">
<field name="name">stock.move.tree2</field>
<field name="model">stock.move</field>
<field name="priority" eval="6"/>
@ -1267,7 +1188,7 @@
</record>
<!-- test -->
<record id="view_move_tree_reception_picking_board" model="ir.ui.view">
<record id="view_move_tree_receipt_picking_board" model="ir.ui.view">
<field name="name">stock.move.tree3</field>
<field name="model">stock.move</field>
<field eval="6" name="priority"/>
@ -1282,18 +1203,18 @@
</field>
</record>
<record id="action_reception_picking_move" model="ir.actions.act_window">
<record id="action_receipt_picking_move" model="ir.actions.act_window">
<field name="name">Incoming Products</field>
<field name="res_model">stock.move</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain" eval="[('picking_id.picking_type_id.code','=','incoming'), ('location_id.usage','!=','internal'), ('location_dest_id.usage', '=', 'internal')]"/>
<field name="view_id" ref="view_move_tree_reception_picking"/>
<field name="view_id" ref="view_move_tree_receipt_picking"/>
<field name="context">{'product_receive': True, 'search_default_future': True}</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to register a product reception.
Click to register a product receipt.
</p><p>
Here you can receive individual products, no matter what
purchase order or picking order they come from. You will find
@ -1793,6 +1714,7 @@
<field name="name">Reordering Rules</field>
<field name="res_model">stock.warehouse.orderpoint</field>
</record>
<record model="ir.actions.act_window" id="product_open_quants">
<field name="context">{'search_default_internal_loc': 1, 'search_default_product_id': active_id, 'search_default_locationgroup':1}</field>
<field name="name">Current Stock</field>

View File

@ -52,7 +52,7 @@
new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'lot_id': lot_a, 'result_package_id': package2, 'product_qty': 120, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_stock')}, context=context)
new_pack2 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'result_package_id': package3, 'product_qty': 60, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_stock')}, context=context)
-
Transfer the reception
Transfer the receipt
-
!python {model: stock.picking}: |
self.do_transfer(cr, uid, [ref('pick1')], context=context)

View File

@ -52,7 +52,7 @@
new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product_neg'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick_neg'), 'lot_id': lot_a, 'result_package_id': package2, 'product_qty': 120, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_stock')}, context=context)
new_pack2 = stock_pack.create(cr, uid, {'product_id': ref('product_neg'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick_neg'), 'result_package_id': package3, 'product_qty': 60, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_stock')}, context=context)
-
Transfer the reception
Transfer the receipt
-
!python {model: stock.picking}: |
self.do_transfer(cr, uid, [ref('pick_neg')], context=context)

View File

@ -55,7 +55,7 @@
})
backorder.do_transfer()
-
I check incomming shipment after reception.
I check incomming shipment after receipt.
-
!python {model: stock.picking}: |
shipment = self.browse(cr, uid, self.search(cr, uid, [('backorder_id', '=', ref("incomming_shipment"))]))[0]

View File

@ -36,7 +36,7 @@
stock_pack.write(cr, uid, record.pack_operation_ids[0].id, {'result_package_id': package1, 'product_qty': 4, 'location_dest_id': ref('stock_location_components')})
new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product_wise'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'product_qty': 6.0, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_14')}, context=context)
-
Transfer the reception
Transfer the receipt
-
!python {model: stock.picking}: |
self.do_transfer(cr, uid, [ref('pick1')], context=context)

View File

@ -26,10 +26,11 @@
import threading
from openerp.osv import fields,osv
from openerp.api import Environment
class procurement_compute(osv.osv_memory):
_name = 'procurement.orderpoint.compute'
_description = 'Automatic Order Point'
_description = 'Compute Minimum Stock Rules'
def _procure_calculation_orderpoint(self, cr, uid, ids, context=None):
@ -40,16 +41,16 @@ class procurement_compute(osv.osv_memory):
@param ids: List of IDs selected
@param context: A standard dictionary
"""
proc_obj = self.pool.get('procurement.order')
#As this function is in a new thread, I need to open a new cursor, because the old one may be closed
new_cr = self.pool.cursor()
user_obj = self.pool.get('res.users')
user = user_obj.browse(new_cr, uid, uid, context=context)
for comp in user.company_ids:
proc_obj._procure_orderpoint_confirm(new_cr, uid, use_new_cursor=new_cr.dbname, company_id = comp.id, context=context)
#close the new cursor
new_cr.close()
return {}
with Environment.manage():
proc_obj = self.pool.get('procurement.order')
#As this function is in a new thread, I need to open a new cursor, because the old one may be closed
new_cr = self.pool.cursor()
user_obj = self.pool.get('res.users')
company_id = user_obj.browse(new_cr, uid, uid, context=context).company_id.id
proc_obj._procure_orderpoint_confirm(new_cr, uid, use_new_cursor=new_cr.dbname, company_id = company_id, context=context)
#close the new cursor
new_cr.close()
return {}
def procure_calculation(self, cr, uid, ids, context=None):
"""
@ -59,6 +60,7 @@ class procurement_compute(osv.osv_memory):
@param ids: List of IDs selected
@param context: A standard dictionary
"""
threaded_calculation = threading.Thread(target=self._procure_calculation_orderpoint, args=(cr, uid, ids, context))
threaded_calculation.start()
return {'type': 'ir.actions.act_window_close'}

View File

@ -105,7 +105,7 @@ class stock_change_product_qty(osv.osv_memory):
'location_id': data.location_id.id,
'product_id': data.product_id.id,
'product_uom_id': data.product_id.uom_id.id,
'th_qty': th_qty,
'theoretical_qty': th_qty,
'prod_lot_id': data.lot_id.id
}
inventory_line_obj.create(cr , uid, line_data, context=context)

View File

@ -31,14 +31,6 @@
<field name="property_stock_account_output" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
</group>
</xpath>
</field>
</record>
<record id="view_product_standard_price_form" model="ir.ui.view">
<field name="name">product.product.standard.price.form.inherit</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<field name="standard_price" position="replace">
<field name="cost_method" groups="stock_account.group_inventory_valuation"/>
<label string="Cost Price" for="standard_price" align="1.0" groups="base.group_user"/>
@ -49,15 +41,6 @@
class="oe_link" groups="stock_account.group_inventory_valuation"/>
</div>
</field>
</field>
</record>
<record id="view_normal_property_acc_form" model="ir.ui.view">
<field name="name">product.normal.stock.acc.property.form.inherit</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="priority">26</field>
<field name="arch" type="xml">
<xpath expr="//group[@name='properties']" position="before">
<group groups="stock_account.group_inventory_valuation">
<separator string="Inventory Valuation" colspan="4"/>
@ -73,6 +56,7 @@
</group>
<newline/>
</xpath>
</field>
</record>
</data>

View File

@ -1,5 +1,5 @@
-
I first create a warehouse with pick-pack-ship and reception in 2 steps
I first create a warehouse with pick-pack-ship and receipt in 2 steps
-
!record {model: stock.warehouse, id: wh_pps}:
name: WareHouse PickPackShip

View File

@ -57,7 +57,7 @@
!assert {model: purchase.order, id: purchase_order_lifo1}:
- state == 'approved'
-
Process the reception of purchase order 1
Process the receipt of purchase order 1
-
!python {model: stock.picking}: |
order = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lifo1"), context=context).picking_ids[0]
@ -72,7 +72,7 @@
-
!workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lifo2}
-
Process the reception of purchase order 2
Process the receipt of purchase order 2
-
!python {model: stock.picking}: |
order = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lifo2"), context=context).picking_ids[0]
@ -81,7 +81,7 @@
Check the standard price should not have changed
-
!python {model: product.product}: |
assert self.browse(cr, uid, ref("product_lifo_icecream")).standard_price == 70.0, 'Standard price as lifo price of second reception incorrect!'
assert self.browse(cr, uid, ref("product_lifo_icecream")).standard_price == 70.0, 'Standard price as lifo price of second receipt incorrect!'
-
Let us send some goods
-

View File

@ -1,5 +1,5 @@
-
Create a warehouse and set it as having a 3-step delivery flow (Pick > Pack > Ship) and a 2-step reception route
Create a warehouse and set it as having a 3-step delivery flow (Pick > Pack > Ship) and a 2-step receipt route
-
-
Create product Combi1 that is MTO / purchase and provide a supplier for it