Merge lp:~unifield-team/unifield-server/utp-999 into lp:unifield-server

Proposed by jftempo
Status: Merged
Merged at revision: 3660
Proposed branch: lp:~unifield-team/unifield-server/utp-999
Merge into: lp:unifield-server
Diff against target: 715 lines (+312/-21)
15 files modified
bin/addons/msf_custom_settings/view/purchase_view.xml (+2/-1)
bin/addons/msf_custom_settings/view/sale_view.xml (+1/-0)
bin/addons/msf_doc_import/view/internal_request_import_line_view.xml (+10/-3)
bin/addons/msf_doc_import/view/purchase_order_import_line_view.xml (+6/-2)
bin/addons/msf_doc_import/view/sale_order_import_lines_view.xml (+5/-1)
bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv (+3/-3)
bin/addons/procurement_request/procurement_request.py (+6/-0)
bin/addons/procurement_request/procurement_request_view.xml (+1/-0)
bin/addons/product/product.py (+17/-4)
bin/addons/product_attributes/product_attributes.py (+1/-0)
bin/addons/product_attributes/product_attributes_view.xml (+1/-0)
bin/addons/purchase_override/purchase.py (+136/-0)
bin/addons/sale_override/sale.py (+113/-0)
bin/addons/supplier_catalogue/product.py (+1/-1)
bin/addons/supplier_catalogue/supplier_catalogue.py (+9/-6)
To merge this branch: bzr merge lp:~unifield-team/unifield-server/utp-999
Reviewer Review Type Date Requested Status
UniField Reviewer Team Pending
Review via email: mp+289465@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/addons/msf_custom_settings/view/purchase_view.xml'
2--- bin/addons/msf_custom_settings/view/purchase_view.xml 2015-07-02 14:20:14 +0000
3+++ bin/addons/msf_custom_settings/view/purchase_view.xml 2016-03-18 10:36:04 +0000
4@@ -109,7 +109,8 @@
5 domain="[('type', '=', 'purchase'), ('in_search', '=', partner_type)]" />
6 <button name="change_currency" string="Change currency" icon="terp-dolar" type="object"
7 attrs="{'invisible': ['|', ('no_line', '=', True), ('state', 'in', ['rfq_updated', 'done', 'cancel', 'confirmed_wait', 'approved', 'except_picking', 'except_invoice'])]}" />
8- </group>
9+ </group>
10+ <button colspan="4" string="Round Qty to SoQ" type="object" name="round_to_soq" icon="gtk-execute" attrs="{'invisible': [('state', '!=', 'draft')]}" />
11 </xpath>
12
13 <xpath expr="/form//field[@name='order_line']" position="attributes">
14
15=== modified file 'bin/addons/msf_custom_settings/view/sale_view.xml'
16--- bin/addons/msf_custom_settings/view/sale_view.xml 2014-05-13 11:58:37 +0000
17+++ bin/addons/msf_custom_settings/view/sale_view.xml 2016-03-18 10:36:04 +0000
18@@ -65,6 +65,7 @@
19 <button name="change_currency" string="Change currency" icon="terp-dolar" type="object"
20 attrs="{'invisible': ['|', ('no_line', '=', True), ('state', 'not in', ['draft', 'validated'])]}" />
21 </group>
22+ <button colspan="4" string="Round Qty to SoQ" type="object" name="round_to_soq" icon="gtk-execute" attrs="{'invisible': [('state', '!=', 'draft')]}" />
23 </xpath>
24
25 <xpath expr="/form//field[@name='order_line']//separator[@string='Taxes']" position="replace" />
26
27=== modified file 'bin/addons/msf_doc_import/view/internal_request_import_line_view.xml'
28--- bin/addons/msf_doc_import/view/internal_request_import_line_view.xml 2014-03-25 14:32:26 +0000
29+++ bin/addons/msf_doc_import/view/internal_request_import_line_view.xml 2016-03-18 10:36:04 +0000
30@@ -10,7 +10,7 @@
31 <field name="arch" type="xml">
32 <form>
33 <field name="to_correct_ok" invisible="1"/>
34- <field name="inactive_product" invisible="1" />
35+ <field name="inactive_product" invisible="1" />
36 <group attrs="{'invisible': [('text_error', '=', False), ('inactive_error', '=', False)]}">
37 <html>
38 <style>
39@@ -68,12 +68,19 @@
40 <field name="inherit_id" ref="procurement_request.procurement_request_form_view" />
41 <field name="arch" type="xml">
42 <data>
43+ <xpath expr="/form" position="attributes">
44+ <attribute name="noteditable">import_in_progress==True</attribute>
45+ </xpath>
46+ <xpath expr="/form//field" position="before">
47+ <field name="import_in_progress" invisible="1" />
48+ </xpath>
49 <xpath expr="/form/notebook/page[@string='Products']/field[@name='order_line']/tree[@string='Products']/field[@name='product_uom']" position="before">
50 <field name="nomenclature_description" readonly="1"/>
51 </xpath>
52 <xpath expr="/form/notebook/page[@string='Products']/field[@name='order_line']/tree[@string='Products']" position="inside">
53 <field name="to_correct_ok" invisible="1"/>
54- <field name="inactive_product" invisible="1" />
55+ <field name="inactive_product" invisible="1" />
56+ <field name="soq_updated" invisible="1" readonly="1" />
57 <group attrs="{'invisible': [('text_error', '=', False), ('inactive_error', '=', False)]}">
58 <html>
59 <style>
60@@ -115,7 +122,7 @@
61 </group>
62 </xpath>
63 <xpath expr="/form/notebook/page[@string='Products']/field[@name='order_line']/tree" position="attributes" >
64- <attribute name="colors">red:to_correct_ok == True or inactive_product == True</attribute>
65+ <attribute name="colors">red:to_correct_ok == True or inactive_product == True;blue: soq_updated == True</attribute>
66 </xpath>
67 <xpath expr="/form/notebook/page[@string='Products']/field[@name='order_line']/tree/field[@name='product_id']" position="before" >
68 <button name="get_error" type="object" icon="gtk-dialog-warning" string="Show error" attrs="{'invisible': [('to_correct_ok', '=', False), ('inactive_product', '=', False)]}" />
69
70=== modified file 'bin/addons/msf_doc_import/view/purchase_order_import_line_view.xml'
71--- bin/addons/msf_doc_import/view/purchase_order_import_line_view.xml 2015-09-09 08:56:04 +0000
72+++ bin/addons/msf_doc_import/view/purchase_order_import_line_view.xml 2016-03-18 10:36:04 +0000
73@@ -11,7 +11,10 @@
74 <field name="arch" type="xml">
75 <data>
76 <xpath expr="/form" position="attributes">
77- <attribute name="noteditable">import_in_progress==True</attribute>
78+ <attribute name="noteditable">import_in_progress==True or update_in_progress==True</attribute>
79+ </xpath>
80+ <xpath expr="/form//field" position="before">
81+ <field name="update_in_progress" invisible="1" readonly="1" />
82 </xpath>
83
84 <xpath expr="/form/notebook/page/field[@name='order_line']/tree/field[@name='currency_id']" position="after">
85@@ -19,9 +22,10 @@
86 </xpath>
87 <xpath expr="/form/notebook/page/field[@name='order_line']/tree//field[@name='default_name']" position="after">
88 <field name="inactive_product" invisible="1" />
89+ <field name="soq_updated" invisible="1" readonly="1" />
90 </xpath>
91 <xpath expr="/form/notebook/page/field[@name='order_line']/tree" position="attributes" >
92- <attribute name="colors">red:to_correct_ok == True or inactive_product == True; #C8C8C8: fake_state == 'cancel'; orange:product_qty == 0.00</attribute>
93+ <attribute name="colors">red:to_correct_ok == True or inactive_product == True; #C8C8C8: fake_state == 'cancel'; orange:product_qty == 0.00;blue: soq_updated == True</attribute>
94 </xpath>
95 <xpath expr="/form/notebook/page[@string='Notes']/field[@name='notes']" position="before">
96 <field name="import_in_progress" readonly="1" invisible="0" />
97
98=== modified file 'bin/addons/msf_doc_import/view/sale_order_import_lines_view.xml'
99--- bin/addons/msf_doc_import/view/sale_order_import_lines_view.xml 2014-01-02 10:40:35 +0000
100+++ bin/addons/msf_doc_import/view/sale_order_import_lines_view.xml 2016-03-18 10:36:04 +0000
101@@ -10,12 +10,16 @@
102 <field name="inherit_id" ref="sale.view_order_form" />
103 <field name="arch" type="xml">
104 <data>
105+ <xpath expr="/form" position="attributes">
106+ <attribute name="noteditable">import_in_progress==True</attribute>
107+ </xpath>
108 <xpath expr="/form/notebook/page[@string='Sales Order']/field[@name='order_line']/tree[@string='Sales Order Lines']" position="inside">
109 <field name="to_correct_ok" invisible="1"/>
110 <field name="inactive_product" invisible="1" />
111+ <field name="soq_updated" invisible="1" readonly="1" />
112 </xpath>
113 <xpath expr="/form/notebook/page[@string='Sales Order']/field[@name='order_line']/tree" position="attributes" >
114- <attribute name="colors">red:to_correct_ok == True or inactive_product == True</attribute>
115+ <attribute name="colors">red:to_correct_ok == True or inactive_product == True;blue:soq_updated == True</attribute>
116 </xpath>
117 <xpath expr="/form/notebook/page[@string='Sales Order']/field[@name='order_line']" position="before" >
118 <group name="import" string=" Import Lines " colspan="4" attrs="{'invisible':[('state', '!=', 'draft')]}">
119
120=== modified file 'bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv'
121--- bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2016-01-25 14:13:18 +0000
122+++ bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2016-03-18 10:36:04 +0000
123@@ -107,8 +107,8 @@
124 msf_sync_data_server.price_list_version,FALSE,TRUE,FALSE,FALSE,bidirectional,Bidirectional,[],"['active', 'date_end', 'date_start', 'name', 'pricelist_id/id']",MISSION,product.pricelist.version,,Price List Version,Valid,,561
125 msf_sync_data_server.country_restrictions,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,[],['name'],MISSION,res.country.restriction,,Country restrictions,Valid,,570
126 msf_sync_data_server.country_code_mapping,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,[],"['instance_id/id', 'mapping_value']",COORDINATIONS,country.export.mapping,,Country Code Mapping,Valid,,571
127-msf_sync_data_server.oc_product_creator_itc_esc_hq,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"['|','|',('international_status','=','ITC'),('international_status','=','ESC'),('international_status','=','HQ')]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'code', 'cold_chain', 'composed_kit', 'xmlid_code', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item', 'international_status', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'narcotic', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'asset_type_id', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'state', 'old_code', 'function_value', 'form_value', 'fit_value', 'standard_ok','transport_ok','volume', 'soq_weight', 'soq_volume']",OC,product.product,,"OC Product (Creator = ITC, ESC or HQ)",Valid,,600
128-msf_sync_data_server.mission_product_creator_local,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('international_status','=','Local')]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'code', 'xmlid_code','cold_chain', 'composed_kit', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item', 'international_status', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'narcotic', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'asset_type_id', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'state', 'old_code', 'function_value', 'form_value', 'fit_value', 'standard_ok','transport_ok','volume','soq_weight','soq_volume']",MISSION,product.product,,Mission Product (Creator = local),Valid,,601
129+msf_sync_data_server.oc_product_creator_itc_esc_hq,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"['|','|',('international_status','=','ITC'),('international_status','=','ESC'),('international_status','=','HQ')]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'code', 'cold_chain', 'composed_kit', 'xmlid_code', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item', 'international_status', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'narcotic', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'asset_type_id', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'state', 'old_code', 'function_value', 'form_value', 'fit_value', 'standard_ok','transport_ok','volume', 'soq_quantity','soq_weight', 'soq_volume']",OC,product.product,,"OC Product (Creator = ITC, ESC or HQ)",Valid,,600
130+msf_sync_data_server.mission_product_creator_local,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('international_status','=','Local')]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'code', 'xmlid_code','cold_chain', 'composed_kit', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item', 'international_status', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'narcotic', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'asset_type_id', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'state', 'old_code', 'function_value', 'form_value', 'fit_value', 'standard_ok','transport_ok','volume','soq_quantity', 'soq_weight','soq_volume']",MISSION,product.product,,Mission Product (Creator = local),Valid,,601
131 msf_sync_data_server.standard_product_list,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,"[('standard_list_ok','=','True')]","['creation_date', 'creator', 'description', 'last_update_date', 'name', 'order_list_print_ok', 'ref', 'standard_list_ok', 'type']",OC,product.list,,Standard Product List,Valid,,605
132 msf_sync_data_server.standard_product_list_line,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,"[('list_id' , 'in', ('product.list', 'id', [('standard_list_ok','=','True')]))]","['comment','list_id/id','ref','name']",OC,product.list.line,,Standard Product List Line,Valid,,606
133 msf_sync_data_server.tax_code,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,[],"['code', 'info', 'name', 'notprintable', 'sign']",OC,account.tax.code,,Tax Code,Valid,,610
134@@ -199,7 +199,7 @@
135 msf_usb_sync_data_server.cp_all_partners_address,TRUE,TRUE,FALSE,TRUE,cp_to_rw,Bidirectional,[],"['active', 'city', 'country_id/id', 'email', 'fax', 'function', 'mobile', 'name', 'partner_id/id', 'phone', 'state_id/id', 'street', 'street2', 'title/id', 'type', 'zip']",USB,res.partner.address,,[MASTER] Partner Address,Valid,,1662
136 msf_usb_sync_data_server.cp_product_price_list,TRUE,TRUE,FALSE,TRUE,cp_to_rw,Bidirectional,[],"['active', 'company_id/id', 'currency_id/id', 'currency_name', 'name', 'type']",USB,product.pricelist,,[MASTER] Product_price_list,Valid,,1670
137 msf_usb_sync_data_server.cp_country_restrictions,TRUE,TRUE,FALSE,TRUE,cp_to_rw,Bidirectional,[],['name'],USB,res.country.restriction,,[MASTER] Country restrictions,Valid,,1680
138-msf_usb_sync_data_server.cp_all_product,TRUE,TRUE,FALSE,TRUE,cp_to_rw,Bidirectional,[],"['alert_time', 'batch_management', 'asset_type_id', 'categ_id/id', 'closed_article', 'code', 'cold_chain', 'composed_kit', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item', 'international_status', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'narcotic', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'xmlid_code', 'state', 'old_code', 'function_value', 'form_value', 'fit_value', 'standard_ok','transport_ok','volume','soq_weight','soq_volume']",USB,product.product,,[MASTER] Products,Valid,,1810
139+msf_usb_sync_data_server.cp_all_product,TRUE,TRUE,FALSE,TRUE,cp_to_rw,Bidirectional,[],"['alert_time', 'batch_management', 'asset_type_id', 'categ_id/id', 'closed_article', 'code', 'cold_chain', 'composed_kit', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item', 'international_status', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'narcotic', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'xmlid_code', 'state', 'old_code', 'function_value', 'form_value', 'fit_value', 'standard_ok','transport_ok','volume','soq_quantity','soq_weight','soq_volume']",USB,product.product,,[MASTER] Products,Valid,,1810
140 msf_usb_sync_data_server.cp_standard_product_list,TRUE,TRUE,TRUE,TRUE,cp_to_rw,Bidirectional,"[('standard_list_ok','=','True')]","['creation_date', 'creator', 'description', 'last_update_date', 'name', 'order_list_print_ok', 'ref', 'standard_list_ok', 'type']",USB,product.list,,[MASTER] Standard Product List,Valid,,1820
141 msf_usb_sync_data_server.cp_standard_product_list_line,TRUE,TRUE,TRUE,TRUE,cp_to_rw,Bidirectional,"[('list_id' , 'in', ('product.list', 'id', [('standard_list_ok','=','True')]))]","['comment','list_id/id','ref','name/id']",USB,product.list.line,,[MASTER] Standard Product List Line,Valid,,1821
142 msf_usb_sync_data_server.cp_composition_kit,TRUE,TRUE,FALSE,TRUE,cp_to_rw,Bidirectional,"[('state','=','completed'),('composition_type','=','theoretical')]","['active', 'composition_creation_date', 'composition_description', 'composition_product_id/id', 'composition_type', 'composition_version', 'composition_version_txt', 'name', 'state']",USB,composition.kit,,[MASTER] Theoretical Kit Composition List,Valid,,1830
143
144=== modified file 'bin/addons/procurement_request/procurement_request.py'
145--- bin/addons/procurement_request/procurement_request.py 2016-02-26 08:21:34 +0000
146+++ bin/addons/procurement_request/procurement_request.py 2016-03-18 10:36:04 +0000
147@@ -466,11 +466,16 @@
148 uom_tbd = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'uom_tbd')[1]
149 nb_lines = 0
150 line_ids = []
151+ reset_soq = []
152 for req in self.browse(cr, uid, ids, context=context):
153 if len(req.order_line) <= 0:
154 raise osv.except_osv(_('Error'), _('You cannot validate an Internal request with no lines !'))
155 for line in req.order_line:
156 line_ids.append(line.id)
157+
158+ if line.soq_updated:
159+ reset_soq.append(line.id)
160+
161 if line.nomen_manda_0.id == nomen_manda_0 \
162 or line.nomen_manda_1.id == nomen_manda_1 \
163 or line.nomen_manda_2.id == nomen_manda_2 \
164@@ -483,6 +488,7 @@
165 raise osv.except_osv(_('Error'), _('Please check the lines : you cannot have "To Be confirmed" for Nomenclature Level". You have %s lines to correct !') % nb_lines)
166 self.log(cr, uid, req.id, _("The internal request '%s' has been validated (nb lines: %s).") % (req.name, len(req.order_line)), context=context)
167 line_obj.update_supplier_on_line(cr, uid, line_ids, context=context)
168+ line_obj.write(cr, uid, reset_soq, {'soq_updated': False,}, context=context)
169 self.write(cr, uid, ids, {'state': 'validated'}, context=context)
170
171 return True
172
173=== modified file 'bin/addons/procurement_request/procurement_request_view.xml'
174--- bin/addons/procurement_request/procurement_request_view.xml 2015-03-17 10:49:00 +0000
175+++ bin/addons/procurement_request/procurement_request_view.xml 2016-03-18 10:36:04 +0000
176@@ -52,6 +52,7 @@
177 </group>
178 <notebook colspan="5">
179 <page string="Products">
180+ <button colspan="4" string="Round Qty to SoQ" type="object" name="round_to_soq" icon="gtk-execute" attrs="{'invisible': [('state', '!=', 'draft')]}" />
181 <field name="order_line" mode="tree,form" colspan="4" nolabel="1" context="{'date_planned': requested_date}" attrs="{'readonly': [('state', 'not in', ['draft', 'validated'])]}">
182 <tree string="Products" editable="top" >
183 <field name="product_id" on_change="requested_product_id_change(product_id, comment)"
184
185=== modified file 'bin/addons/product/product.py'
186--- bin/addons/product/product.py 2016-03-03 13:37:41 +0000
187+++ bin/addons/product/product.py 2016-03-18 10:36:04 +0000
188@@ -316,9 +316,15 @@
189 return res and res[1] or False
190
191 def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id):
192+ res = {
193+ 'value': {
194+ 'soq_quantity': 0.00,
195+ }
196+ }
197 if uom_id:
198- return {'value': {'uom_po_id': uom_id}}
199- return False
200+ res['value']['uom_po_id'] = uom_id
201+
202+ return res
203
204 _defaults = {
205 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'product.template', context=c),
206@@ -497,13 +503,20 @@
207 return super(product_product, self).unlink(cr, uid, unlink_ids, context=context)
208
209 def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id):
210+ res = {
211+ 'value': {
212+ },
213+ }
214+ if uom_id:
215+ res['value']['soq_quantity'] = 0.00
216+
217 if uom_id and uom_po_id:
218 uom_obj=self.pool.get('product.uom')
219 uom=uom_obj.browse(cursor,user,[uom_id])[0]
220 uom_po=uom_obj.browse(cursor,user,[uom_po_id])[0]
221 if uom.category_id.id != uom_po.category_id.id:
222- return {'value': {'uom_po_id': uom_id}}
223- return False
224+ res['value']['uom_po_id'] = uom_id
225+ return res
226
227 def _check_ean_key(self, cr, uid, ids, context=None):
228 for product in self.read(cr, uid, ids, ['ean13'], context=context):
229
230=== modified file 'bin/addons/product_attributes/product_attributes.py'
231--- bin/addons/product_attributes/product_attributes.py 2016-01-25 10:53:51 +0000
232+++ bin/addons/product_attributes/product_attributes.py 2016-03-18 10:36:04 +0000
233@@ -451,6 +451,7 @@
234 'standard_ok': fields.boolean(string='Standard'),
235 'soq_weight': fields.float(digits=(16,5), string='SoQ Weight'),
236 'soq_volume': fields.float(digits=(16,5), string='SoQ Volume'),
237+ 'soq_quantity': fields.float(digits=(16,2), string='SoQ Quantity'),
238 'vat_ok': fields.function(_get_vat_ok, method=True, type='boolean', string='VAT OK', store=False, readonly=True),
239 }
240
241
242=== modified file 'bin/addons/product_attributes/product_attributes_view.xml'
243--- bin/addons/product_attributes/product_attributes_view.xml 2015-05-29 15:33:12 +0000
244+++ bin/addons/product_attributes/product_attributes_view.xml 2016-03-18 10:36:04 +0000
245@@ -119,6 +119,7 @@
246 <field name="perishable"/>
247 <field name="batch_management" on_change="onchange_batch_management(batch_management)" />
248 <separator string="UOM" colspan="2"/>
249+ <field name="soq_quantity" />
250 <field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)" domain="[('compatible_product_id', '=', active_id)]" />
251 <field name="uom_po_id" string="Exchangeable Unit of Measure" domain="[('compatible_product_id', '=', active_id)]" />
252 </group>
253
254=== modified file 'bin/addons/purchase_override/purchase.py'
255--- bin/addons/purchase_override/purchase.py 2016-03-14 10:26:38 +0000
256+++ bin/addons/purchase_override/purchase.py 2016-03-18 10:36:04 +0000
257@@ -24,6 +24,9 @@
258 from tools.translate import _
259 import netsvc
260 import time
261+import threading
262+import logging
263+import pooler
264 from mx.DateTime import Parser
265 from mx.DateTime import RelativeDateTime
266 from time import strftime
267@@ -401,6 +404,10 @@
268 type='boolean',
269 store=False,
270 ),
271+ 'update_in_progress': fields.boolean(
272+ string='Update in progress',
273+ readonly=True,
274+ ),
275 }
276
277 _defaults = {
278@@ -420,6 +427,7 @@
279 'canceled_end': False,
280 'split_po': False,
281 'vat_ok': lambda obj, cr, uid, context: obj.pool.get('unifield.setup.configuration').get_config(cr, uid).vat_ok,
282+ 'update_in_progress': False,
283 }
284
285 def _check_po_from_fo(self, cr, uid, ids, context=None):
286@@ -1056,6 +1064,7 @@
287 ids = [ids]
288
289 todo = []
290+ reset_soq = []
291
292 for po in self.browse(cr, uid, ids, context=context):
293 line_error = []
294@@ -1086,6 +1095,8 @@
295 for line in po.order_line:
296 if line.state=='draft':
297 todo.append(line.id)
298+ if line.soq_updated:
299+ reset_soq.append(line.id)
300
301 message = _("Purchase order '%s' is validated.") % (po.name,)
302 self.log(cr, uid, po.id, message)
303@@ -1093,6 +1104,7 @@
304 self._hook_confirm_order_update_corresponding_so(cr, uid, ids, context=context, po=po)
305
306 po_line_obj.action_confirm(cr, uid, todo, context)
307+ po_line_obj.write(cr, uid, reset_soq, {'soq_updated': False,}, context=context)
308
309 self.write(cr, uid, ids, {'state' : 'confirmed',
310 'validator' : uid,
311@@ -2395,6 +2407,121 @@
312
313 return {'type': 'ir.actions.act_window_close'}
314
315+ def round_to_soq(self, cr, uid, ids, context=None):
316+ """
317+ Create a new thread to check for each line of the order if the quantity
318+ is compatible with the SoQ rounding of the supplier catalogue or
319+ product. If not compatible, update the quantity to match with SoQ rounding.
320+ :param cr: Cursor to the database
321+ :param uid: ID of the res.users that calls the method
322+ :param ids: List of ID of sale.order to check and update
323+ :param context: Context of the call
324+ :return: True
325+ """
326+ th = threading.Thread(
327+ target=self._do_round_to_soq,
328+ args=(cr, uid, ids, context, True),
329+ )
330+ th.start()
331+ th.join(5.0)
332+
333+ return True
334+
335+ def _do_round_to_soq(self, cr, uid, ids, context=None, use_new_cursor=False):
336+ """
337+ Check for each line of the order if the quantity is compatible
338+ with the SoQ rounding of the supplier catalogue or product. If
339+ not compatible, update the quantity to match with SoQ rounding.
340+ :param cr: Cursor to the database
341+ :param uid: ID of the res.users that calls the method
342+ :param ids: List of ID of sale.order to check and update
343+ :param context: Context of the call
344+ :param use_new_cursor: True if this method is called into a new thread
345+ :return: True
346+ """
347+ pol_obj = self.pool.get('purchase.order.line')
348+ uom_obj = self.pool.get('product.uom')
349+ sup_obj = self.pool.get('product.supplierinfo')
350+
351+ if context is None:
352+ context = {}
353+
354+ if isinstance(ids, (int, long)):
355+ ids = [ids]
356+
357+ if use_new_cursor:
358+ cr = pooler.get_db(cr.dbname).cursor()
359+
360+ try:
361+ self.write(cr, uid, ids, {
362+ 'update_in_progress': True,
363+ }, context=context)
364+ if use_new_cursor:
365+ cr.commit()
366+
367+ pol_ids = pol_obj.search(cr, uid, [
368+ ('order_id', 'in', ids),
369+ ('product_id', '!=', False),
370+ ], context=context)
371+
372+ to_update = {}
373+ for pol in pol_obj.browse(cr, uid, pol_ids, context=context):
374+ # Check only products with defined SoQ quantity
375+ sup_ids = sup_obj.search(cr, uid, [
376+ ('name', '=', pol.order_id.partner_id.id),
377+ ('product_id', '=', pol.product_id.id),
378+ ], context=context)
379+ if not sup_ids and not pol.product_id.soq_quantity:
380+ continue
381+
382+ # Get SoQ value
383+ soq = pol.product_id.soq_quantity
384+ soq_uom = pol.product_id.uom_id
385+ if sup_ids:
386+ for sup in sup_obj.browse(cr, uid, sup_ids, context=context):
387+ for pcl in sup.pricelist_ids:
388+ if pcl.rounding and pcl.min_quantity <= pol.product_qty:
389+ soq = pcl.rounding
390+ soq_uom = pcl.uom_id
391+
392+ if not soq:
393+ continue
394+
395+ # Get line quantity in SoQ UoM
396+ line_qty = pol.product_qty
397+ if pol.product_uom.id != soq_uom.id:
398+ line_qty = uom_obj._compute_qty_obj(cr, uid, pol.product_uom, pol.product_qty, soq_uom, context=context)
399+
400+ good_quantity = 0
401+ if line_qty % soq:
402+ good_quantity = (line_qty - (line_qty % soq)) + soq
403+
404+ if good_quantity and pol.product_uom.id != soq_uom.id:
405+ good_quantity = uom_obj._compute_qty_obj(cr, uid, soq_uom, good_quantity, pol.product_uom, context=context)
406+
407+ if good_quantity:
408+ to_update.setdefault(good_quantity, [])
409+ to_update[good_quantity].append(pol.id)
410+
411+ for qty, line_ids in to_update.iteritems():
412+ pol_obj.write(cr, uid, line_ids, {
413+ 'product_qty': qty,
414+ 'soq_updated': True,
415+ }, context=context)
416+ except Exception as e:
417+ logger = logging.getLogger('purchase.order.round_to_soq')
418+ logger.error(e)
419+ finally:
420+ self.write(cr, uid, ids, {
421+ 'update_in_progress': False,
422+ }, context=context)
423+
424+ if use_new_cursor:
425+ cr.commit()
426+ cr.close()
427+
428+ return True
429+
430 purchase_order()
431
432
433@@ -2931,6 +3058,10 @@
434 order='NO_ORDER', context=context)
435 exp_sol_obj.unlink(cr, uid, exp_sol_ids, context=context)
436
437+ # Remove SoQ updated flag in case of manual modification
438+ if not 'soq_updated' in vals:
439+ vals['soq_updated'] = False
440+
441 for line in self.browse(cr, uid, ids, context=context):
442 new_vals = vals.copy()
443 # check qty
444@@ -3449,6 +3580,10 @@
445 string='Linked FO line',
446 store=False,
447 ),
448+ 'soq_updated': fields.boolean(
449+ string='SoQ updated',
450+ readonly=True,
451+ ),
452 }
453
454 _defaults = {
455@@ -3458,6 +3593,7 @@
456 'change_price_ok': lambda *a: True,
457 'is_line_split': False, # UTP-972: by default not a split line
458 'from_fo': lambda self, cr, uid, c: not c.get('rfq_ok', False) and c.get('from_fo', False),
459+ 'soq_updated': False,
460 }
461
462 def product_uom_change(self, cr, uid, ids, pricelist, product, qty, uom,
463
464=== modified file 'bin/addons/sale_override/sale.py'
465--- bin/addons/sale_override/sale.py 2016-02-19 10:57:13 +0000
466+++ bin/addons/sale_override/sale.py 2016-03-18 10:36:04 +0000
467@@ -29,9 +29,11 @@
468 import time
469 from tools.translate import _
470 import logging
471+import threading
472 from workflow.wkf_expr import _eval_expr
473
474 import decimal_precision as dp
475+import pooler
476
477 from sale_override import SALE_ORDER_STATE_SELECTION
478 from sale_override import SALE_ORDER_SPLIT_SELECTION
479@@ -897,6 +899,7 @@
480 ids = [ids]
481
482 order_brw_list = self.browse(cr, uid, ids, context=context)
483+ reset_soq = []
484
485 # 1/ Check validity of analytic distribution
486 self.analytic_distribution_checks(cr, uid, order_brw_list)
487@@ -905,6 +908,8 @@
488 line_ids = []
489 for line in order.order_line:
490 line_ids.append(line.id)
491+ if line.soq_updated:
492+ reset_soq.append(line.id)
493 no_price_lines = []
494 if order.order_type == 'regular':
495 cr.execute('SELECT line_number FROM sale_order_line WHERE (price_unit*product_uom_qty < 0.01 OR price_unit = 0.00) AND order_id = %s', (order.id,))
496@@ -946,6 +951,8 @@
497 if not order.procurement_request and order.split_type_sale_order == 'original_sale_order':
498 line_obj.update_supplier_on_line(cr, uid, line_ids, context=context)
499
500+ line_obj.write(cr, uid, reset_soq, {'soq_updated': False,}, context=context)
501+
502 self.write(cr, uid, ids, {
503 'state': 'validated',
504 'validated_date': time.strftime('%Y-%m-%d %H:%M:%S'),
505@@ -2245,6 +2252,103 @@
506 def _manual_create_sync_message(self, cr, uid, res_id, return_info, rule_method, context=None):
507 return
508
509+ def round_to_soq(self, cr, uid, ids, context=None):
510+ """
511+ Create a new thread to check for each line of the order if the quantity
512+ is compatible with SoQ rounding of the product. If not compatible,
513+ update the quantity to match with SoQ rounding.
514+ :param cr: Cursor to the database
515+ :param uid: ID of the res.users that calls the method
516+ :param ids: List of ID of sale.order to check and update
517+ :param context: Context of the call
518+ :return: True
519+ """
520+ th = threading.Thread(
521+ target=self._do_round_to_soq,
522+ args=(cr, uid, ids, context, True),
523+ )
524+ th.start()
525+ th.join(5.0)
526+
527+ return True
528+
529+ def _do_round_to_soq(self, cr, uid, ids, context=None, use_new_cursor=False):
530+ """
531+ Check for each line of the order if the quantity is compatible
532+ with SoQ rounding of the product. If not compatible, update the
533+ quantity to match with SoQ rounding.
534+ :param cr: Cursor to the database
535+ :param uid: ID of the res.users that calls the method
536+ :param ids: List of ID of sale.order to check and update
537+ :param context: Context of the call
538+ :param use_new_cursor: True if this method is called into a new thread
539+ :return: True
540+ """
541+ sol_obj = self.pool.get('sale.order.line')
542+ uom_obj = self.pool.get('product.uom')
543+
544+ if context is None:
545+ context = {}
546+
547+ if isinstance(ids, (int, long)):
548+ ids = [ids]
549+
550+ if use_new_cursor:
551+ cr = pooler.get_db(cr.dbname).cursor()
552+
553+ try:
554+ self.write(cr, uid, ids, {
555+ 'import_in_progress': True,
556+ }, context=context)
557+ if use_new_cursor:
558+ cr.commit()
559+
560+ sol_ids = sol_obj.search(cr, uid, [
561+ ('order_id', 'in', ids),
562+ ('product_id', '!=', False),
563+ ], context=context)
564+
565+ to_update = {}
566+ for sol in sol_obj.browse(cr, uid, sol_ids, context=context):
567+ # Check only products with defined SoQ quantity
568+ if not sol.product_id.soq_quantity:
569+ continue
570+
571+ # Get line quantity in product UoM
572+ line_qty = sol.product_uom_qty
573+ if sol.product_uom.id != sol.product_id.uom_id.id:
574+ line_qty = uom_obj._compute_qty_obj(cr, uid, sol.product_uom, sol.product_uom_qty, sol.product_id.uom_id, context=context)
575+
576+ good_quantity = 0
577+ if line_qty % sol.product_id.soq_quantity:
578+ good_quantity = (line_qty - (line_qty % sol.product_id.soq_quantity)) + sol.product_id.soq_quantity
579+
580+ if good_quantity and sol.product_uom.id != sol.product_id.uom_id.id:
581+ good_quantity = uom_obj._compute_qty_obj(cr, uid, sol.product_id.uom_id, good_quantity, sol.product_uom, context=context)
582+
583+ if good_quantity:
584+ to_update.setdefault(good_quantity, [])
585+ to_update[good_quantity].append(sol.id)
586+
587+ for qty, line_ids in to_update.iteritems():
588+ sol_obj.write(cr, uid, line_ids, {
589+ 'product_uom_qty': qty,
590+ 'soq_updated': True,
591+ }, context=context)
592+ except Exception as e:
593+ logger = logging.getLogger('sale.order.round_to_soq')
594+ logger.error(e)
595+ finally:
596+ self.write(cr, uid, ids, {
597+ 'import_in_progress': False,
598+ }, context=context)
599+
600+ if use_new_cursor:
601+ cr.commit()
602+ cr.close()
603+
604+ return True
605+
606 sale_order()
607
608
609@@ -2297,11 +2401,16 @@
610 help='If the line has been canceled/removed on the splitted FO',
611 ),
612 'vat_ok': fields.function(_get_vat_ok, method=True, type='boolean', string='VAT OK', store=False, readonly=True),
613+ 'soq_updated': fields.boolean(
614+ string='SoQ updated',
615+ readonly=True,
616+ ),
617 }
618
619 _defaults = {
620 'is_line_split': False, # UTP-972: By default set False, not split
621 'vat_ok': lambda obj, cr, uid, context: obj.pool.get('unifield.setup.configuration').get_config(cr, uid).vat_ok,
622+ 'soq_updated': False,
623 }
624
625 def ask_unlink(self, cr, uid, ids, context=None):
626@@ -2889,6 +2998,10 @@
627
628 self.check_empty_line(cr, uid, ids, vals, context=context)
629
630+ # Remove SoQ updated flag in case of manual modification
631+ if not 'soq_updated' in vals:
632+ vals['soq_updated'] = False
633+
634 res = super(sale_order_line, self).write(cr, uid, ids, vals, context=context)
635
636 return res
637
638=== modified file 'bin/addons/supplier_catalogue/product.py'
639--- bin/addons/supplier_catalogue/product.py 2016-01-05 15:11:26 +0000
640+++ bin/addons/supplier_catalogue/product.py 2016-03-18 10:36:04 +0000
641@@ -245,7 +245,7 @@
642
643 _columns = {
644 'uom_id': fields.many2one('product.uom', string='UoM', required=True),
645- 'rounding': fields.float(digits=(16,2), string='Rounding',
646+ 'rounding': fields.float(digits=(16,2), string='SoQ Rounding',
647 help='The ordered quantity must be a multiple of this rounding value.'),
648 'min_order_qty': fields.float(digits=(16, 2), string='Min. Order Qty'),
649 'valid_from': fields.date(string='Valid from'),
650
651=== modified file 'bin/addons/supplier_catalogue/supplier_catalogue.py'
652--- bin/addons/supplier_catalogue/supplier_catalogue.py 2016-01-25 10:53:51 +0000
653+++ bin/addons/supplier_catalogue/supplier_catalogue.py 2016-03-18 10:36:04 +0000
654@@ -1,5 +1,5 @@
655+##############################################################################
656 # -*- coding: utf-8 -*-
657-##############################################################################
658 #
659 # OpenERP, Open Source Management Solution
660 # Copyright (C) 2011 MSF, TeMPO Consulting
661@@ -370,7 +370,7 @@
662 readonly=True, help='Indicate if the catalogue is currently active.'),
663 'file_to_import': fields.binary(string='File to import', filters='*.xml',
664 help="""The file should be in XML Spreadsheet 2003 format. The columns should be in this order :
665- Product Code*, Product Description, Product UoM*, Min Quantity*, Unit Price*, Rounding, Min Order Qty, Comment."""),
666+ Product Code*, Product Description, Product UoM*, Min Quantity*, Unit Price*, SoQ Rounding, Min Order Qty, Comment."""),
667 'data': fields.binary(string='File with errors',),
668 'filename': fields.char(string='Lines not imported', size=256),
669 'filename_template': fields.char(string='Template', size=256),
670@@ -449,7 +449,7 @@
671 Warning: len(columns_header) == len(lines_not_imported)
672 """
673 columns_header = [('Product code*', 'string'), ('Product description', 'string'), ('Product UoM*', 'string'),
674- ('Min Quantity*', 'number'), ('Unit Price*', 'number'), ('Rounding', 'number'), ('Min Order Qty', 'number'),
675+ ('Min Quantity*', 'number'), ('Unit Price*', 'number'), ('SoQ Rounding', 'number'), ('Min Order Qty', 'number'),
676 ('Comment', 'string')]
677 lines_not_imported = [] # list of list
678 t_dt = type(now())
679@@ -510,7 +510,7 @@
680 to_correct_ok = False
681 row_len = len(row)
682 if row_len != 8:
683- error_list_line.append(_("You should have exactly 8 columns in this order: Product code*, Product description, Product UoM*, Min Quantity*, Unit Price*, Rounding, Min Order Qty, Comment."))
684+ error_list_line.append(_("You should have exactly 8 columns in this order: Product code*, Product description, Product UoM*, Min Quantity*, Unit Price*, SoQ Rounding, Min Order Qty, Comment."))
685 comment = []
686 p_comment = False
687 #Product code
688@@ -597,7 +597,7 @@
689 if row.cells[5] and row.cells[5].type in ['int', 'float']:
690 p_rounding = row.cells[5].data
691 else:
692- error_list_line.append(_('Please, format the line number %s, column "Rounding".') % (line_num,))
693+ error_list_line.append(_('Please, format the line number %s, column "SoQ rounding".') % (line_num,))
694
695 #Product Min Order Qty
696 if not len(row.cells)>=7 or not row.cells[6].data:
697@@ -960,7 +960,7 @@
698 'line_uom_id': fields.many2one('product.uom', string='Product UoM', required=True,
699 help='UoM of the product used to get this unit price.'),
700 'unit_price': fields.float(string='Unit Price', required=True, digits_compute=dp.get_precision('Purchase Price Computation')),
701- 'rounding': fields.float(digits=(16,2), string='Rounding',
702+ 'rounding': fields.float(digits=(16,2), string='SoQ rounding',
703 help='The ordered quantity must be a multiple of this rounding value.'),
704 'min_order_qty': fields.float(digits=(16,2), string='Min. Order Qty'),
705 'comment': fields.char(size=64, string='Comment'),
706@@ -1007,6 +1007,9 @@
707 if min_order_qty:
708 res = uom_obj._change_round_up_qty(cr, uid, uom_id, min_order_qty, 'min_order_qty', result=res)
709
710+ res.setdefault('value', {})
711+ res['value']['rounding'] = 0.00
712+
713 return res
714
715 def onChangeSearchNomenclature(self, cr, uid, line_id, position, line_type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, num=True, context=None):

Subscribers

People subscribed via source and target branches

to all changes: