Merge lp:~jfb-tempo-consulting/unifield-server/US-12719 into lp:unifield-server
- US-12719
- Merge into trunk
Proposed by
jftempo
Status: | Merged |
---|---|
Merged at revision: | 6286 |
Proposed branch: | lp:~jfb-tempo-consulting/unifield-server/US-12719 |
Merge into: | lp:unifield-server |
Diff against target: |
1905 lines (+1362/-90) (has conflicts) 11 files modified
bin/addons/msf_audittrail/data/audittrail_data_products.yml (+1/-1) bin/addons/msf_profile/data/patches.xml (+8/-0) bin/addons/msf_profile/data/ud_default_oc_value.csv (+177/-0) bin/addons/msf_profile/msf_profile.py (+74/-0) bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv (+1/-1) bin/addons/product_attributes/product_attributes.py (+92/-17) bin/addons/product_attributes/product_attributes_data.xml (+20/-0) bin/addons/product_attributes/product_attributes_view.xml (+17/-1) bin/addons/product_attributes/unidata_sync.py (+714/-69) bin/addons/product_attributes/unidata_sync.xml (+159/-1) tools/UD/create_sql.py (+99/-0) Text conflict in bin/addons/msf_profile/data/patches.xml |
To merge this branch: | bzr merge lp:~jfb-tempo-consulting/unifield-server/US-12719 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
UniField Reviewer Team | Pending | ||
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
- 6272. By jftempo
-
[FIX] nomenclature label / don't touch product accounts / dangerous_good added / single_use 'Not Applicable' to no_know / thermosensitive: empty to No
- 6273. By jftempo
-
[MERGE] trunk
- 6274. By jftempo
-
UD Pull: deactivate cron
- 6275. By jftempo
-
Remove fit_value, form_value, function_value from pull
- 6276. By jftempo
-
dangeours_goods: typo
- 6277. By jftempo
-
Single use: NotĀ applicable mapped to No
- 6278. By jftempo
-
add publishonweb=False
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/addons/msf_audittrail/data/audittrail_data_products.yml' |
2 | --- bin/addons/msf_audittrail/data/audittrail_data_products.yml 2024-04-12 14:41:30 +0000 |
3 | +++ bin/addons/msf_audittrail/data/audittrail_data_products.yml 2024-05-13 13:47:13 +0000 |
4 | @@ -6,7 +6,7 @@ |
5 | object_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', 'product.product')], context=context) |
6 | rule_id = self.search(cr, uid, [('name', '=', name)], context=context) |
7 | if object_ids: |
8 | - fields = ['default_code', 'old_code', 'new_code', 'type', 'subtype', 'categ_id', 'international_status', 'state', 'active', 'perishable', 'batch_management', 'heat_sensitive_item', 'controlled_substance', 'dangerous_goods', 'standard_ok', 'justification_code_id', 'restricted_country', 'country_restriction', 'form_value', 'fit_value', 'function_value', 'procure_delay', 'soq_volume', 'soq_weight', 'soq_quantity', 'valuation', 'donation_expense_account', 'replace_product_id', 'replaced_by_product_id', 'oc_subscription', 'state_ud', 'un_code', 'msfid', 'oc_validation', 'oc_validation_date', 'oc_devalidation_date', 'oc_devalidation_reason', 'oc_comments', 'oc_project_restrictions', 'oc_country_restrictions'] |
9 | + fields = ['default_code', 'old_code', 'new_code', 'type', 'subtype', 'categ_id', 'international_status', 'state', 'active', 'perishable', 'batch_management', 'heat_sensitive_item', 'controlled_substance', 'dangerous_goods', 'standard_ok', 'justification_code_id', 'restricted_country', 'country_restriction', 'form_value', 'fit_value', 'function_value', 'procure_delay', 'soq_volume', 'soq_weight', 'soq_quantity', 'valuation', 'donation_expense_account', 'replace_product_id', 'replaced_by_product_id', 'oc_subscription', 'state_ud', 'un_code', 'hs_code', 'msfid', 'oc_validation', 'oc_validation_date', 'oc_devalidation_date', 'oc_devalidation_reason', 'oc_comments', 'oc_project_restrictions', 'oc_country_restrictions', 'closed_article', 'cold_chain', 'manufacturer_ref', 'manufacturer_txt', 'xmlid_code', 'product_catalog_path', 'short_shelf_life', 'single_use', 'sterilized'] |
10 | fields_ids = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=' ,'product.product'), ('name', 'in', fields)], context=context) |
11 | vals = {'name': name, |
12 | 'object_id': object_ids[0], |
13 | |
14 | === modified file 'bin/addons/msf_profile/data/patches.xml' |
15 | --- bin/addons/msf_profile/data/patches.xml 2024-05-06 12:42:13 +0000 |
16 | +++ bin/addons/msf_profile/data/patches.xml 2024-05-13 13:47:13 +0000 |
17 | @@ -1044,6 +1044,7 @@ |
18 | <field name="method">us_12294_force_email_popup</field> |
19 | </record> |
20 | |
21 | + |
22 | <!-- UF33.0 --> |
23 | <record id="us_12074_gdpr_remove_personal_data_from_track_changess" model="patch.scripts"> |
24 | <field name="method">us_12074_gdpr_remove_personal_data_from_track_changes</field> |
25 | @@ -1053,10 +1054,17 @@ |
26 | <field name="method">us_11135_set_pol_confirmation_date</field> |
27 | </record> |
28 | |
29 | +<<<<<<< TREE |
30 | <record id="us_12350_is_default_update" model="patch.scripts"> |
31 | <field name="method">us_12350_is_default_update</field> |
32 | </record> |
33 | |
34 | +======= |
35 | + <record id="us_12826_unidata_pull_info" model="patch.scripts"> |
36 | + <field name="method">us_12826_unidata_pull_info</field> |
37 | + </record> |
38 | + |
39 | +>>>>>>> MERGE-SOURCE |
40 | |
41 | </data> |
42 | </openerp> |
43 | |
44 | === added file 'bin/addons/msf_profile/data/ud_default_oc_value.csv' |
45 | --- bin/addons/msf_profile/data/ud_default_oc_value.csv 1970-01-01 00:00:00 +0000 |
46 | +++ bin/addons/msf_profile/data/ud_default_oc_value.csv 2024-05-13 13:47:13 +0000 |
47 | @@ -0,0 +1,177 @@ |
48 | +Identifier,MSF Entity,Attribute,OC Rules,Default Value |
49 | +96,OCA,perishable,[OCA] D - Drugs | ,Yes |
50 | +98,OCA,perishable,[OCA] K - Kits | MED - Medical kits,Yes |
51 | +99,OCA,perishable,[OCA] N - Nutrition | FOO - Food commodities,Yes |
52 | +100,OCA,perishable,[OCA] N - Nutrition | FOS - Specialized food,Yes |
53 | +101,OCA,perishable,"[OCA] S - Renewable medical supplies | CTD - Catheters, tubes and drains",Yes |
54 | +102,OCA,perishable,[OCA] S - Renewable medical supplies | INS - Injection supplies,Yes |
55 | +103,OCA,perishable,[OCA] S - Renewable medical supplies | LAS - Laboratory reagents,Yes |
56 | +104,OCA,perishable,[OCA] S - Renewable medical supplies | SDT - Stand-alone diagnostic tests,Yes |
57 | +105,OCA,perishable,[OCA] S - Renewable medical supplies | SUT - Sutures,Yes |
58 | +106,OCA,batch_management,[OCA] D - Drugs | ,Yes |
59 | +107,OCA,batch_management,[OCA] K - Kits | MED - Medical kits,Yes |
60 | +108,OCA,batch_management,[OCA] N - Nutrition | FOO - Food commodities,Yes |
61 | +109,OCA,batch_management,[OCA] N - Nutrition | FOS - Specialized food,Yes |
62 | +110,OCA,batch_management,"[OCA] S - Renewable medical supplies | CTD - Catheters, tubes and drains",Yes |
63 | +111,OCA,batch_management,[OCA] S - Renewable medical supplies | INS - Injection supplies,Yes |
64 | +112,OCA,batch_management,[OCA] S - Renewable medical supplies | LAS - Laboratory reagents,Yes |
65 | +113,OCA,batch_management,[OCA] S - Renewable medical supplies | SDT - Stand-alone diagnostic tests,Yes |
66 | +114,OCA,batch_management,[OCA] S - Renewable medical supplies | SUT - Sutures,Yes |
67 | +115,OCA,type,[OCA] X - Services | ,Service with Reception |
68 | +116,OCA,subtype,[OCA] K - Kits | ,Kit/Module |
69 | +117,OCA,procure_method,[OCA] X - Services | ,Make to Order |
70 | +196,OCA,procure_method,[OCA] L - Library | ,Make to Order |
71 | +197,OCA,perishable,[OCA] S - Renewable medical supplies | AST - Antibiotic Susceptibility Testing Items,Yes |
72 | +198,OCA,perishable,[OCA] S - Renewable medical supplies | BCM - Bacteriological culture media,Yes |
73 | +199,OCA,perishable,[OCA] S - Renewable medical supplies | BID - Bacterial identification system,Yes |
74 | +200,OCA,perishable,[OCA] S - Renewable medical supplies | BQC - Bacterial strains Quality Control,Yes |
75 | +201,OCA,perishable,[OCA] S - Renewable medical supplies | DIS - Disinfectants,Yes |
76 | +202,OCA,batch_management,[OCA] S - Renewable medical supplies | AST - Antibiotic Susceptibility Testing Items,Yes |
77 | +203,OCA,batch_management,[OCA] S - Renewable medical supplies | BCM - Bacteriological culture media,Yes |
78 | +204,OCA,batch_management,[OCA] S - Renewable medical supplies | BID - Bacterial identification system,Yes |
79 | +205,OCA,batch_management,[OCA] S - Renewable medical supplies | BQC - Bacterial strains Quality Control,Yes |
80 | +206,OCA,batch_management,[OCA] S - Renewable medical supplies | DIS - Disinfectants,Yes |
81 | +6,OCB,perishable,[OCB] D - Drugs | ,Yes |
82 | +7,OCB,batch_management,[OCB] D - Drugs | ,Yes |
83 | +25,OCB,subtype,[OCB] K - Kits | ,Kit/Module |
84 | +26,OCB,perishable,[OCB] N - Nutrition | ,Yes |
85 | +27,OCB,batch_management,[OCB] N - Nutrition | ,Yes |
86 | +31,OCB,perishable,"[OCB] S - Renewable medical supplies | CTD - Catheters, tubes and drains",Yes |
87 | +32,OCB,batch_management,"[OCB] S - Renewable medical supplies | CTD - Catheters, tubes and drains",Yes |
88 | +51,OCB,perishable,[OCB] S - Renewable medical supplies | INS - Injection supplies,Yes |
89 | +52,OCB,batch_management,[OCB] S - Renewable medical supplies | INS - Injection supplies,Yes |
90 | +56,OCB,perishable,[OCB] S - Renewable medical supplies | LAS - Laboratory reagents,Yes |
91 | +57,OCB,batch_management,[OCB] S - Renewable medical supplies | LAS - Laboratory reagents,Yes |
92 | +66,OCB,perishable,[OCB] S - Renewable medical supplies | MSU - Small medical supplies,Yes |
93 | +67,OCB,batch_management,[OCB] S - Renewable medical supplies | MSU - Small medical supplies,Yes |
94 | +71,OCB,perishable,[OCB] S - Renewable medical supplies | SUT - Sutures,Yes |
95 | +72,OCB,batch_management,[OCB] S - Renewable medical supplies | SUT - Sutures,Yes |
96 | +93,OCB,procure_method,[OCB] X - Services | ,Make to Order |
97 | +94,OCB,type,[OCB] X - Services | ,Service with Reception |
98 | +95,OCB,subtype,[OCB] X - Services | , |
99 | +136,OCB,batch_management,[OCB] K - Kits | MED - Medical kits,Yes |
100 | +137,OCB,batch_management,[OCB] K - Kits | SUD - Dental surgical instruments sets,Yes |
101 | +138,OCB,batch_management,[OCB] K - Kits | SUI - Internal fixation instruments set,Yes |
102 | +139,OCB,batch_management,[OCB] K - Kits | SUO - Ophthalmic instruments boxes,Yes |
103 | +140,OCB,batch_management,[OCB] K - Kits | SUR - Surgical instruments sets,Yes |
104 | +141,OCB,perishable,[OCB] K - Kits | MED - Medical kits,Yes |
105 | +142,OCB,perishable,[OCB] K - Kits | SUD - Dental surgical instruments sets,Yes |
106 | +143,OCB,perishable,[OCB] K - Kits | SUI - Internal fixation instruments set,Yes |
107 | +144,OCB,perishable,[OCB] K - Kits | SUO - Ophthalmic instruments boxes,Yes |
108 | +145,OCB,perishable,[OCB] K - Kits | SUR - Surgical instruments sets,Yes |
109 | +146,OCB,perishable,[OCB] S - Renewable medical supplies | AST - Antibiotic Susceptibility Testing Items,Yes |
110 | +147,OCB,perishable,[OCB] S - Renewable medical supplies | BCM - Bacteriological culture media,Yes |
111 | +148,OCB,perishable,[OCB] S - Renewable medical supplies | BID - Bacterial identification system,Yes |
112 | +149,OCB,perishable,[OCB] S - Renewable medical supplies | BQC - Bacterial strains Quality Control,Yes |
113 | +154,OCB,perishable,[OCB] S - Renewable medical supplies | SCO - Surgical consumables,Yes |
114 | +155,OCB,perishable,[OCB] S - Renewable medical supplies | SDT - Stand-alone diagnostic tests,Yes |
115 | +156,OCB,perishable,[OCB] S - Renewable medical supplies | SUR - Surgical instruments for single use,Yes |
116 | +158,OCB,perishable,"[OCB] S - Renewable medical supplies | TSS - Transport, storage and sampling systems of biological specim",Yes |
117 | +159,OCB,perishable,[OCB] S - Renewable medical supplies | DIS - Disinfectants,Yes |
118 | +160,OCB,batch_management,[OCB] S - Renewable medical supplies | AST - Antibiotic Susceptibility Testing Items,Yes |
119 | +161,OCB,batch_management,[OCB] S - Renewable medical supplies | BCM - Bacteriological culture media,Yes |
120 | +162,OCB,batch_management,[OCB] S - Renewable medical supplies | BID - Bacterial identification system,Yes |
121 | +163,OCB,batch_management,[OCB] S - Renewable medical supplies | BQC - Bacterial strains Quality Control,Yes |
122 | +165,OCB,batch_management,[OCB] S - Renewable medical supplies | SCO - Surgical consumables,Yes |
123 | +166,OCB,batch_management,[OCB] S - Renewable medical supplies | SDT - Stand-alone diagnostic tests,Yes |
124 | +167,OCB,batch_management,[OCB] S - Renewable medical supplies | SUR - Surgical instruments for single use,Yes |
125 | +168,OCB,batch_management,"[OCB] S - Renewable medical supplies | TSS - Transport, storage and sampling systems of biological specim",Yes |
126 | +169,OCB,batch_management,[OCB] S - Renewable medical supplies | DIS - Disinfectants,Yes |
127 | +263,OCB,procure_method,[OCB] L - Library | ,Make to Order |
128 | +118,OCG,batch_management,[OCG] D - Drugs | ,Yes |
129 | +119,OCG,batch_management,[OCG] K - Kits | MED - Medical kits,Yes |
130 | +120,OCG,batch_management,[OCG] N - Nutrition | FOO - Food commodities,Yes |
131 | +121,OCG,batch_management,[OCG] N - Nutrition | ,Yes |
132 | +122,OCG,batch_management,"[OCG] S - Renewable medical supplies | CTD - Catheters, tubes and drains",Yes |
133 | +123,OCG,batch_management,[OCG] S - Renewable medical supplies | INS - Injection supplies,Yes |
134 | +124,OCG,batch_management,[OCG] S - Renewable medical supplies | LAS - Laboratory reagents,Yes |
135 | +125,OCG,batch_management,[OCG] S - Renewable medical supplies | MSU - Small medical supplies,Yes |
136 | +126,OCG,batch_management,[OCG] S - Renewable medical supplies | SUT - Sutures,Yes |
137 | +127,OCG,perishable,[OCG] D - Drugs | ,Yes |
138 | +128,OCG,perishable,[OCG] K - Kits | MED - Medical kits,Yes |
139 | +129,OCG,perishable,[OCG] N - Nutrition | ,Yes |
140 | +131,OCG,perishable,"[OCG] S - Renewable medical supplies | CTD - Catheters, tubes and drains",Yes |
141 | +132,OCG,perishable,[OCG] S - Renewable medical supplies | INS - Injection supplies,Yes |
142 | +133,OCG,perishable,[OCG] S - Renewable medical supplies | LAS - Laboratory reagents,Yes |
143 | +134,OCG,perishable,[OCG] S - Renewable medical supplies | MSU - Small medical supplies,Yes |
144 | +135,OCG,perishable,[OCG] S - Renewable medical supplies | SUT - Sutures,Yes |
145 | +170,OCG,batch_management,[OCG] S - Renewable medical supplies | DRE - Dressings,Yes |
146 | +171,OCG,batch_management,[OCG] S - Renewable medical supplies | SDT - Stand-alone diagnostic tests,Yes |
147 | +172,OCG,perishable,[OCG] S - Renewable medical supplies | DRE - Dressings,Yes |
148 | +173,OCG,perishable,[OCG] S - Renewable medical supplies | SDT - Stand-alone diagnostic tests,Yes |
149 | +174,OCG,procure_method,[OCG] L - Library | ,Make to Order |
150 | +175,OCG,procure_method,[OCG] X - Services | ,Make to Order |
151 | +176,OCG,subtype,[OCG] K - Kits | ,Kit/Module |
152 | +177,OCG,type,[OCG] X - Services | ,Service with Reception |
153 | +178,OCG,batch_management,[OCG] S - Renewable medical supplies | AST - Antibiotic Susceptibility Testing Items,Yes |
154 | +179,OCG,batch_management,[OCG] S - Renewable medical supplies | BCM - Bacteriological culture media,Yes |
155 | +180,OCG,batch_management,[OCG] S - Renewable medical supplies | BID - Bacterial identification system,Yes |
156 | +181,OCG,batch_management,[OCG] S - Renewable medical supplies | BQC - Bacterial strains Quality Control,Yes |
157 | +183,OCG,batch_management,[OCG] S - Renewable medical supplies | SCO - Surgical consumables,Yes |
158 | +184,OCG,batch_management,[OCG] S - Renewable medical supplies | SUR - Surgical instruments for single use,Yes |
159 | +185,OCG,batch_management,"[OCG] S - Renewable medical supplies | TSS - Transport, storage and sampling systems of biological specim",Yes |
160 | +186,OCG,batch_management,[OCG] S - Renewable medical supplies | DIS - Disinfectants,Yes |
161 | +187,OCG,perishable,[OCG] S - Renewable medical supplies | AST - Antibiotic Susceptibility Testing Items,Yes |
162 | +188,OCG,perishable,[OCG] S - Renewable medical supplies | BCM - Bacteriological culture media,Yes |
163 | +189,OCG,perishable,[OCG] S - Renewable medical supplies | BID - Bacterial identification system,Yes |
164 | +190,OCG,perishable,[OCG] S - Renewable medical supplies | BQC - Bacterial strains Quality Control,Yes |
165 | +191,OCG,perishable,[OCG] S - Renewable medical supplies | SCO - Surgical consumables,Yes |
166 | +192,OCG,perishable,[OCG] S - Renewable medical supplies | SUR - Surgical instruments for single use,Yes |
167 | +193,OCG,perishable,"[OCG] S - Renewable medical supplies | TSS - Transport, storage and sampling systems of biological specim",Yes |
168 | +194,OCG,perishable,[OCG] S - Renewable medical supplies | DIS - Disinfectants,Yes |
169 | +207,OCP,type,[OCP] X - Services | ,Service with Reception |
170 | +208,OCP,subtype,[OCP] X - Services | , |
171 | +209,OCP,subtype,[OCP] K - Kits | ,Kit/Module |
172 | +210,OCP,procure_method,[OCP] X - Services | ,Make to Order |
173 | +211,OCP,perishable,[OCP] D - Drugs | ,Yes |
174 | +212,OCP,perishable,[OCP] E - Medical equipment | ANE - Anaesthesia equipment,Yes |
175 | +213,OCP,perishable,[OCP] E - Medical equipment | DIM - Diagnostic Imaging equipment,Yes |
176 | +214,OCP,perishable,[OCP] E - Medical equipment | LAB - Laboratory equipment,Yes |
177 | +215,OCP,perishable,[OCP] E - Medical equipment | LAE - Electrical laboratory equipment,Yes |
178 | +216,OCP,perishable,[OCP] E - Medical equipment | SUR - Surgical instruments,Yes |
179 | +217,OCP,perishable,[OCP] K - Kits | MED - Medical kits,Yes |
180 | +218,OCP,perishable,[OCP] N - Nutrition | FOO - Food commodities,Yes |
181 | +219,OCP,perishable,[OCP] N - Nutrition | FOS - Specialized food,Yes |
182 | +220,OCP,perishable,[OCP] S - Renewable medical supplies | AST - Antibiotic Susceptibility Testing Items,Yes |
183 | +221,OCP,perishable,[OCP] S - Renewable medical supplies | BCM - Bacteriological culture media,Yes |
184 | +222,OCP,perishable,[OCP] S - Renewable medical supplies | BID - Bacterial identification system,Yes |
185 | +223,OCP,perishable,[OCP] S - Renewable medical supplies | BQC - Bacterial strains Quality Control,Yes |
186 | +224,OCP,perishable,"[OCP] S - Renewable medical supplies | CTD - Catheters, tubes and drains",Yes |
187 | +225,OCP,perishable,[OCP] S - Renewable medical supplies | DIM - Diagnostic imaging supplies,Yes |
188 | +226,OCP,perishable,[OCP] S - Renewable medical supplies | DIS - Disinfectants,Yes |
189 | +227,OCP,perishable,[OCP] S - Renewable medical supplies | DRE - Dressings,Yes |
190 | +228,OCP,perishable,[OCP] S - Renewable medical supplies | INS - Injection supplies,Yes |
191 | +229,OCP,perishable,[OCP] S - Renewable medical supplies | LAS - Laboratory reagents,Yes |
192 | +230,OCP,perishable,[OCP] S - Renewable medical supplies | MSU - Small medical supplies,Yes |
193 | +231,OCP,perishable,[OCP] S - Renewable medical supplies | PPE - Personal protective equipment for medical activities,Yes |
194 | +232,OCP,perishable,[OCP] S - Renewable medical supplies | SCO - Surgical consumables,Yes |
195 | +233,OCP,perishable,[OCP] S - Renewable medical supplies | SDT - Stand-alone diagnostic tests,Yes |
196 | +234,OCP,perishable,[OCP] S - Renewable medical supplies | SUR - Surgical instruments for single use,Yes |
197 | +235,OCP,perishable,[OCP] S - Renewable medical supplies | SUT - Sutures,Yes |
198 | +236,OCP,perishable,"[OCP] S - Renewable medical supplies | TSS - Transport, storage and sampling systems of biological specim",Yes |
199 | +237,OCP,batch_management,[OCP] D - Drugs | ,Yes |
200 | +238,OCP,batch_management,[OCP] E - Medical equipment | ANE - Anaesthesia equipment,Yes |
201 | +239,OCP,batch_management,[OCP] E - Medical equipment | DIM - Diagnostic Imaging equipment,Yes |
202 | +240,OCP,batch_management,[OCP] E - Medical equipment | LAB - Laboratory equipment,Yes |
203 | +241,OCP,batch_management,[OCP] E - Medical equipment | LAE - Electrical laboratory equipment,Yes |
204 | +242,OCP,batch_management,[OCP] E - Medical equipment | SUR - Surgical instruments,Yes |
205 | +243,OCP,batch_management,[OCP] K - Kits | MED - Medical kits,Yes |
206 | +244,OCP,batch_management,[OCP] N - Nutrition | FOO - Food commodities,Yes |
207 | +245,OCP,batch_management,[OCP] N - Nutrition | FOS - Specialized food,Yes |
208 | +246,OCP,batch_management,[OCP] S - Renewable medical supplies | AST - Antibiotic Susceptibility Testing Items,Yes |
209 | +247,OCP,batch_management,[OCP] S - Renewable medical supplies | BCM - Bacteriological culture media,Yes |
210 | +248,OCP,batch_management,[OCP] S - Renewable medical supplies | BID - Bacterial identification system,Yes |
211 | +249,OCP,batch_management,[OCP] S - Renewable medical supplies | BQC - Bacterial strains Quality Control,Yes |
212 | +250,OCP,batch_management,"[OCP] S - Renewable medical supplies | CTD - Catheters, tubes and drains",Yes |
213 | +251,OCP,batch_management,[OCP] S - Renewable medical supplies | DIM - Diagnostic imaging supplies,Yes |
214 | +252,OCP,batch_management,[OCP] S - Renewable medical supplies | DIS - Disinfectants,Yes |
215 | +253,OCP,batch_management,[OCP] S - Renewable medical supplies | DRE - Dressings,Yes |
216 | +254,OCP,batch_management,[OCP] S - Renewable medical supplies | INS - Injection supplies,Yes |
217 | +255,OCP,batch_management,[OCP] S - Renewable medical supplies | LAS - Laboratory reagents,Yes |
218 | +256,OCP,batch_management,[OCP] S - Renewable medical supplies | MSU - Small medical supplies,Yes |
219 | +257,OCP,batch_management,[OCP] S - Renewable medical supplies | PPE - Personal protective equipment for medical activities,Yes |
220 | +258,OCP,batch_management,[OCP] S - Renewable medical supplies | SCO - Surgical consumables,Yes |
221 | +259,OCP,batch_management,[OCP] S - Renewable medical supplies | SDT - Stand-alone diagnostic tests,Yes |
222 | +260,OCP,batch_management,[OCP] S - Renewable medical supplies | SUR - Surgical instruments for single use,Yes |
223 | +261,OCP,batch_management,[OCP] S - Renewable medical supplies | SUT - Sutures,Yes |
224 | +262,OCP,batch_management,"[OCP] S - Renewable medical supplies | TSS - Transport, storage and sampling systems of biological specim",Yes |
225 | |
226 | === modified file 'bin/addons/msf_profile/msf_profile.py' |
227 | --- bin/addons/msf_profile/msf_profile.py 2024-05-06 12:42:13 +0000 |
228 | +++ bin/addons/msf_profile/msf_profile.py 2024-05-13 13:47:13 +0000 |
229 | @@ -58,6 +58,80 @@ |
230 | } |
231 | |
232 | # UF33.0 |
233 | + def us_12826_unidata_pull_info(self, cr, uid, *a, **b): |
234 | + entity_obj = self.pool.get('sync.client.entity') |
235 | + instance = self.pool.get('res.users').browse(cr, uid, uid).company_id.instance_id |
236 | + if entity_obj and instance and instance.level == 'section': |
237 | + if instance.instance in ('OCP_HQ', 'OCBHQ', 'HQ_OCA', 'OCG_HQ'): |
238 | + ent = entity_obj.get_entity(cr, uid) |
239 | + oc = ent.oc.upper() |
240 | + values_mapping = { |
241 | + 'Yes': 't', |
242 | + 'Kit/Module': 'kit', |
243 | + 'Make to Order': 'make_to_order', |
244 | + 'Service with Reception': 'service_recep', |
245 | + '': '', |
246 | + } |
247 | + csv_file_name = os.path.join(os.path.abspath(tools.config['root_path']), 'addons/msf_profile/data/ud_default_oc_value.csv') |
248 | + line_number = 0 |
249 | + with open(csv_file_name, 'r', newline='') as f: |
250 | + c = csv.reader(f, quotechar='"', delimiter=',') |
251 | + for line in c: |
252 | + line_number += 1 |
253 | + if not line or line[1] != oc: |
254 | + continue |
255 | + nomen = line[3][5:].strip() |
256 | + all_nom = nomen.split('|') |
257 | + level = 1 |
258 | + parent_id = False |
259 | + |
260 | + if line[4] not in values_mapping: |
261 | + self._logger.warn('Line number %s, value %s not found' % (line_number, line[4])) |
262 | + return False |
263 | + for nom in all_nom: |
264 | + nom = nom.strip()[0:63] |
265 | + if nom: |
266 | + cond = '' |
267 | + params = [nom.strip(), level] |
268 | + if parent_id: |
269 | + cond = ' and n.parent_id in %s' |
270 | + params.append(tuple(parent_id)) |
271 | + params[0] = '%%%s' % nom |
272 | + |
273 | + cr.execute(""" |
274 | + select |
275 | + n.id |
276 | + from |
277 | + product_nomenclature n |
278 | + left join ir_translation t on t.lang='en_MF' and t.name='product.nomenclature,name' and t.res_id = n.id |
279 | + where |
280 | + coalesce(t.value, n.name) like %s and |
281 | + n.level=%s |
282 | + """+cond, tuple(params)) # not_a_user_entry |
283 | + if not cr.rowcount: |
284 | + self._logger.warn('Line number %s, nomen %s not found' % (line_number, nom)) |
285 | + return False |
286 | + parent_id = [x[0] for x in cr.fetchall()] |
287 | + level += 1 |
288 | + for n_id in parent_id: |
289 | + query = "insert into unidata_default_product_value (field, value, nomenclature, create_date) values (%s, %s, %s, NOW());" |
290 | + values = (line[2], values_mapping[line[4]], n_id) |
291 | + cr.execute(query, values) |
292 | + |
293 | + cr.execute("update product_cold_chain set ud_code=code") |
294 | + cr.execute("update product_cold_chain set ud_code='CT3+' where id in (select res_id from ir_model_data where name='product_attributes_cold_20')") |
295 | + |
296 | + cr.execute("update unidata_sync set is_active='f'") |
297 | + cr.execute("update ir_cron set active='f' where model='unidata.sync'") |
298 | + # set next UD sync as full sync |
299 | + param_obj = self.pool.get('ir.config_parameter') |
300 | + param_obj.set_param(cr, 1, 'LAST_UD_DATE_SYNC', '') |
301 | + param_obj.set_param(cr, 1, 'LAST_MSFID_SYNC','') |
302 | + |
303 | + |
304 | + |
305 | + return True |
306 | + |
307 | def us_12074_gdpr_remove_personal_data_from_track_changes(self, cr, uid, *a, **b): |
308 | ''' |
309 | GDPR - Remove from staff track changes the fields removed from US-7791 |
310 | |
311 | === modified file 'bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv' |
312 | --- bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2023-11-17 14:06:38 +0000 |
313 | +++ bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2024-05-13 13:47:13 +0000 |
314 | @@ -132,7 +132,7 @@ |
315 | 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 |
316 | msf_sync_data_server.country_restrictions,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,[],['name'],MISSION,res.country.restriction,,Country restrictions,Valid,,570 |
317 | 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 |
318 | -msf_sync_data_server.oc_product_creator_itc_esc_hq,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"['|','|','|',('international_status','=','UniData'),('international_status','=','ITC'),('international_status','=','ESC'),('international_status','=','HQ'), ('active', 'in', ['t','f'])]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'manufacturer_txt', 'manufacturer_ref', 'code', 'cold_chain/id', '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/id', 'international_status/id', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'controlled_substance', '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', 'state_ud', 'old_code', 'new_code', 'function_value', 'form_value', 'fit_value', 'standard_ok', 'local_from_hq', 'transport_ok','volume', 'soq_quantity', 'soq_weight', 'soq_volume', 'msfid', 'oc_subscription', 'donation_expense_account/id', 'oc_validation', 'oc_validation_date', 'oc_devalidation_date', 'oc_devalidation_reason', 'oc_comments', 'oc_project_restrictions/id', 'oc_country_restrictions/id']",OC,product.product,,"OC Product (Creator = ITC, ESC, UniData or HQ)",Valid,,600 |
319 | +msf_sync_data_server.oc_product_creator_itc_esc_hq,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"['|','|','|',('international_status','=','UniData'),('international_status','=','ITC'),('international_status','=','ESC'),('international_status','=','HQ'), ('active', 'in', ['t','f'])]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'manufacturer_txt', 'manufacturer_ref', 'code', 'cold_chain/id', '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/id', 'international_status/id', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'controlled_substance', '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', 'hs_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'state', 'state_ud', 'old_code', 'new_code', 'function_value', 'form_value', 'fit_value', 'standard_ok', 'local_from_hq', 'transport_ok','volume', 'soq_quantity', 'soq_weight', 'soq_volume', 'msfid', 'oc_subscription', 'donation_expense_account/id', 'oc_validation', 'oc_validation_date', 'oc_devalidation_date', 'oc_devalidation_reason', 'oc_comments', 'oc_project_restrictions/id', 'oc_country_restrictions/id']",OC,product.product,,"OC Product (Creator = ITC, ESC, UniData or HQ)",Valid,,600 |
320 | msf_sync_data_server.mission_product_creator_local,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('international_status','=','Local'), ('active', 'in', ['t','f'])]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'manufacturer_txt', 'manufacturer_ref', 'code', 'xmlid_code','cold_chain/id', 'composed_kit', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item/id', 'international_status/id', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'controlled_substance', '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', 'new_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 |
321 | msf_sync_data_server.oc_product_creator_itc_esc_hq_active,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"['|','|','|',('international_status','=','UniData'),('international_status','=','ITC'),('international_status','=','ESC'),('international_status','=','HQ'), ('active', 'in', ['t','f']), ('active_change_date', '!=', False)]","['active', 'local_from_hq', 'local_activation_from_merge']",OC,product.product,,"OC Product Active (Creator = ITC, ESC, UniData or HQ)",Valid,,602 |
322 | msf_sync_data_server.mission_product_creator_local_active,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('international_status','=','Local'), ('active', 'in', ['t','f']), ('active_change_date', '!=', False)]","['active']",MISSION,product.product,,Mission Product Active (Creator = local),Valid,,603 |
323 | |
324 | === modified file 'bin/addons/product_attributes/product_attributes.py' |
325 | --- bin/addons/product_attributes/product_attributes.py 2024-05-02 09:35:15 +0000 |
326 | +++ bin/addons/product_attributes/product_attributes.py 2024-05-13 13:47:13 +0000 |
327 | @@ -186,6 +186,7 @@ |
328 | _name = "product.cold_chain" |
329 | _columns = { |
330 | 'code': fields.char('Code', size=256), |
331 | + 'ud_code': fields.char(string='Code', size=256), |
332 | 'name': fields.char('Name', size=256, required=True, translate=1), |
333 | 'cold_chain': fields.boolean('Cold Chain'), |
334 | 'mapped_to': fields.many2one('product.cold_chain', string='Mapped to', readonly=1), |
335 | @@ -668,6 +669,39 @@ |
336 | dom += [ '&', ('batch_management', '=', False), ('perishable', '=', True)] |
337 | return dom |
338 | |
339 | + def _search_incompatible_oc_default_values(self, cr, uid, obj, name, args, context=None): |
340 | + dom = [] |
341 | + oc_def = self.pool.get('unidata.default_product_value') |
342 | + for arg in args: |
343 | + if arg[1] == '=': |
344 | + if not arg[2]: |
345 | + raise osv.except_osv(_('Warning'), _('This filter is not implemented')) |
346 | + |
347 | + oc_def_ids = oc_def.search(cr, uid, [], context=context) |
348 | + elif arg[1] == 'in': |
349 | + if not isinstance(arg[2], list): |
350 | + raise osv.except_osv(_('Warning'), _('This filter is not implemented')) |
351 | + oc_def_ids = arg[2] |
352 | + else: |
353 | + raise osv.except_osv(_('Warning'), _('This filter is not implemented')) |
354 | + |
355 | + temp_dom = [] |
356 | + for oc_val in oc_def.browse(cr, uid, oc_def_ids, context=context): |
357 | + value = oc_val.value |
358 | + if value == 'f': |
359 | + value = False |
360 | + temp_dom.append(['&', ('nomen_manda_%d' % oc_val.nomenclature.level, '=', oc_val.nomenclature.id), (oc_val.field, '!=', value)]) |
361 | + |
362 | + ud_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'product_attributes', 'int_6')[1] |
363 | + if temp_dom: |
364 | + dom += temp_dom[0] |
365 | + for d in temp_dom[1:]: |
366 | + dom.insert(0, '|') |
367 | + dom += d |
368 | + dom = ['&', ('international_status', '=', ud_id)] + dom |
369 | + |
370 | + return dom |
371 | + |
372 | def _search_show_ud(self, cr, uid, obj, name, args, context=None): |
373 | dom = [] |
374 | for arg in args: |
375 | @@ -1082,7 +1116,8 @@ |
376 | ), |
377 | 'oc_subscription': fields.boolean(string='OC Subscription'), |
378 | # TODO: validation on 'un_code' field |
379 | - 'un_code': fields.char('UN Code', size=7), |
380 | + 'un_code': fields.char('UN Code', size=32), |
381 | + 'hs_code': fields.char('HS Code', size=12, readonly=1), |
382 | 'gmdn_code' : fields.char('GMDN Code', size=5), |
383 | 'gmdn_description' : fields.char('GMDN Description', size=64), |
384 | 'life_time': fields.integer('Product Life Time', |
385 | @@ -1217,6 +1252,8 @@ |
386 | 'in_mml_instance': fields.function(tools.misc.get_fake, method=True, type='many2one', relation='msf.instance', string='MML Valid for instance', domain=[('state', '=', 'active'), ('level', '!=', 'section')]), |
387 | 'mml_restricted_instance': fields.function(tools.misc.get_fake, method=True, type='many2one', relation='msf.instance', string='MML Restricted to instance', domain=[('state', '=', 'active'), ('level', '!=', 'section')]), |
388 | 'in_msl_instance': fields.function(_get_valid_msl_instance, method=True, type='many2many', relation='unifield.instance', domain=[('uf_active', '=', True)], string='MSL Valid for instance'), |
389 | + |
390 | + 'incompatible_oc_default_values': fields.function(tools.misc.get_fake, method=True, type='boolean', string='Incompatible OC default', fnct_search=_search_incompatible_oc_default_values), |
391 | } |
392 | |
393 | |
394 | @@ -3468,22 +3505,21 @@ |
395 | return True |
396 | |
397 | def pull_ud(self, cr, uid, ids, context=None): |
398 | - ud = unidata_sync.ud_sync(cr, uid, self.pool, logger=logging.getLogger('single-ud-sync'), max_retries=1, context=context) |
399 | - |
400 | - code_updated = [] |
401 | - update = False |
402 | - for x in self.read(cr, uid, ids, ['msfid', 'default_code'], context=context): |
403 | - if x['msfid']: |
404 | - try: |
405 | - ud.update_products(q_filter='msfIdentifier=%d'%x['msfid'], record_date=False) |
406 | - code_updated.append(x['default_code']) |
407 | - if not update: |
408 | - update = x['id'] |
409 | - except requests.exceptions.HTTPError as e: |
410 | - raise osv.except_osv(_('Error'), _('Unidata error: %s, did you configure the UniData sync ?') % e.response) |
411 | - if code_updated: |
412 | - self.log(cr, uid, update, _('%s updated from UniData') % ', '.join(code_updated)) |
413 | - return True |
414 | + for x in self.read(cr, uid, ids, ['msfid'] , context=context): |
415 | + wiz_id = self.pool.get('product.pull_single_ud').create(cr, uid, {'msfid': x['msfid'] or False}, context=context) |
416 | + return { |
417 | + 'type': 'ir.actions.act_window', |
418 | + 'res_model': 'product.pull_single_ud', |
419 | + 'res_id': wiz_id, |
420 | + 'view_type': 'form', |
421 | + 'view_mode': 'form', |
422 | + 'target': 'new', |
423 | + 'context': context, |
424 | + 'height': '190px', |
425 | + 'width': '420px', |
426 | + } |
427 | + |
428 | + |
429 | |
430 | def open_mml_nonconform_report(self, cr, uid, ids, context=None): |
431 | instance_level = self.pool.get('res.company')._get_instance_level(cr, uid) |
432 | @@ -3839,5 +3875,44 @@ |
433 | |
434 | product_ask_activate_wizard() |
435 | |
436 | +class product_pull_single_ud(osv.osv_memory): |
437 | + _name = 'product.pull_single_ud' |
438 | + rec_name = 'msfid' |
439 | + _columns = { |
440 | + 'msfid': fields.integer_null('MSFID'), |
441 | + } |
442 | + |
443 | + def pull_product(self, cr, uid, ids, context=None): |
444 | + session_obj = self.pool.get('unidata.sync.log') |
445 | + act_obj = self.pool.get('ir.actions.act_window') |
446 | + for x in self.read(cr, uid, ids, ['msfid'], context=context): |
447 | + if x['msfid']: |
448 | + session_id = session_obj.create(cr, uid, {'manual_single': True, 'server': 'ud', 'start_date': fields.datetime.now(), 'state': 'running', 'sync_type': 'single', 'msfid_min': x['msfid']}, context=context) |
449 | + ud = unidata_sync.ud_sync(cr, uid, self.pool, logger=logging.getLogger('single-ud-sync'), max_retries=1, context=context) |
450 | + try: |
451 | + trash1, nb_prod, updated, total_nb_created, total_nb_errors = ud.update_products(q_filter='msfIdentifier=%d'%x['msfid'], record_date=False, session_id=session_id) |
452 | + except requests.exceptions.HTTPError as e: |
453 | + raise osv.except_osv(_('Error'), _('Unidata error: %s, did you configure the UniData sync ?') % e.response) |
454 | + except Exception: |
455 | + raise |
456 | + else: |
457 | + raise osv.except_osv(_('Error'), _('Error: msfid is required')) |
458 | + |
459 | + session_obj.write(cr, uid, session_id, {'end_date': fields.datetime.now(), 'state': 'done', 'number_products_pulled': nb_prod, 'number_products_updated': updated, 'number_products_created': total_nb_created, 'number_products_errors': total_nb_errors}, context=context) |
460 | + |
461 | + p_ids = self.pool.get('product.product').search(cr, uid, [('msfid', '=', x['msfid'])], context=context) |
462 | + if p_ids: |
463 | + if len(p_ids) == 1: |
464 | + view = act_obj.open_view_from_xmlid(cr, uid, 'product.product_normal_action', ['form', 'tree'], context=context) |
465 | + view['res_id'] = p_ids[0] |
466 | + else: |
467 | + view = act_obj.open_view_from_xmlid(cr, uid, 'product.product_normal_action', context=context) |
468 | + view['domain'] = [('id','in', p_ids)] |
469 | + else: |
470 | + view = act_obj.open_view_from_xmlid(cr, uid, 'product_attributes.unidata_sync_log_action', ['form', 'tree'], new_tab=True, context=context) |
471 | + view['res_id'] = session_id |
472 | + return view |
473 | + |
474 | +product_pull_single_ud() |
475 | |
476 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
477 | |
478 | === modified file 'bin/addons/product_attributes/product_attributes_data.xml' |
479 | --- bin/addons/product_attributes/product_attributes_data.xml 2023-08-29 09:17:44 +0000 |
480 | +++ bin/addons/product_attributes/product_attributes_data.xml 2024-05-13 13:47:13 +0000 |
481 | @@ -133,36 +133,43 @@ |
482 | <!-- new code --> |
483 | <record model="product.cold_chain" id="cold_14"> |
484 | <field name="code">0208</field> |
485 | + <field name="ud_code">0208</field> |
486 | <field name="name">0208 - Cold Chain / Refrigerated 2-8°C</field> |
487 | <field name="cold_chain" eval="True" /> |
488 | </record> |
489 | <record model="product.cold_chain" id="cold_15"> |
490 | <field name="code">1525</field> |
491 | + <field name="ud_code">1525</field> |
492 | <field name="name">1525 - Controlled Room Temperature 15-25°C</field> |
493 | <field name="cold_chain" eval="False" /> |
494 | </record> |
495 | <record model="product.cold_chain" id="cold_16"> |
496 | <field name="code">CT25</field> |
497 | + <field name="ud_code">CT25</field> |
498 | <field name="name">CT25 - Controlled Temperature 2-25°C</field> |
499 | <field name="cold_chain" eval="False" /> |
500 | </record> |
501 | <record model="product.cold_chain" id="cold_17"> |
502 | <field name="code">CT30</field> |
503 | + <field name="ud_code">CT30</field> |
504 | <field name="name">CT30 - Controlled Temperature 2-30°C</field> |
505 | <field name="cold_chain" eval="False" /> |
506 | </record> |
507 | <record model="product.cold_chain" id="cold_18"> |
508 | <field name="code">F-20</field> |
509 | + <field name="ud_code">F-20</field> |
510 | <field name="name">F-20 - Frozen <-20°C</field> |
511 | <field name="cold_chain" eval="False" /> |
512 | </record> |
513 | <record model="product.cold_chain" id="cold_19"> |
514 | <field name="code">FSRT</field> |
515 | + <field name="ud_code">FSRT</field> |
516 | <field name="name">FSRT - Frozen for Storage, Refrigerated for Transport</field> |
517 | <field name="cold_chain" eval="False" /> |
518 | </record> |
519 | <record model="product.cold_chain" id="cold_20"> |
520 | <field name="code">CT30</field> |
521 | + <field name="ud_code">CT30</field> |
522 | <field name="name">CT30 - Controlled Temperature 2-30°C</field> |
523 | <field name="cold_chain" eval="False" /> |
524 | </record> |
525 | @@ -170,78 +177,91 @@ |
526 | <!-- old code --> |
527 | <record model="product.cold_chain" id="cold_1"> |
528 | <field name="code">*</field> |
529 | + <field name="ud_code">*</field> |
530 | <field name="name">* - Keep Cool: used for a kit or article containing cold chain module or item(s)</field> |
531 | <field name="cold_chain" eval="True" /> |
532 | <field name="mapped_to" ref="cold_14" /> |
533 | </record> |
534 | <record model="product.cold_chain" id="cold_2"> |
535 | <field name="code">*0</field> |
536 | + <field name="ud_code">*0</field> |
537 | <field name="name">*0 - Problem if any window blue</field> |
538 | <field name="cold_chain" eval="True" /> |
539 | <field name="mapped_to" ref="cold_14" /> |
540 | </record> |
541 | <record model="product.cold_chain" id="cold_3"> |
542 | <field name="code">*0F</field> |
543 | + <field name="ud_code">*0F</field> |
544 | <field name="name">*0F - Problem if any window blue or Freeze-tag = ALARM</field> |
545 | <field name="cold_chain" eval="True" /> |
546 | <field name="mapped_to" ref="cold_14" /> |
547 | </record> |
548 | <record model="product.cold_chain" id="cold_4"> |
549 | <field name="code">*A</field> |
550 | + <field name="ud_code">*A</field> |
551 | <field name="name">*A - Problem if A, B, C and/or D blue = ALARM</field> |
552 | <field name="cold_chain" eval="True" /> |
553 | <field name="mapped_to" ref="cold_14" /> |
554 | </record> |
555 | <record model="product.cold_chain" id="cold_5"> |
556 | <field name="code">*AF</field> |
557 | + <field name="ud_code">*AF</field> |
558 | <field name="name">*AF - Problem if A, B, C and/or D blue or Freeze-tag = ALARM</field> |
559 | <field name="cold_chain" eval="True" /> |
560 | <field name="mapped_to" ref="cold_14" /> |
561 | </record> |
562 | <record model="product.cold_chain" id="cold_6"> |
563 | <field name="code">*B</field> |
564 | + <field name="ud_code">*B</field> |
565 | <field name="name">*B - Problem if B, C and/or D blue = ALARM</field> |
566 | <field name="cold_chain" eval="True" /> |
567 | <field name="mapped_to" ref="cold_14" /> |
568 | </record> |
569 | <record model="product.cold_chain" id="cold_7"> |
570 | <field name="code">*BF</field> |
571 | + <field name="ud_code">*BF</field> |
572 | <field name="name">*BF - Problem if B, C and/or D blue or Freeze-tag = ALARM</field> |
573 | <field name="cold_chain" eval="True" /> |
574 | <field name="mapped_to" ref="cold_14" /> |
575 | </record> |
576 | <record model="product.cold_chain" id="cold_8"> |
577 | <field name="code">*C</field> |
578 | + <field name="ud_code">*C</field> |
579 | <field name="name">*C - Problem if C and D blue</field> |
580 | <field name="cold_chain" eval="True" /> |
581 | <field name="mapped_to" ref="cold_14" /> |
582 | </record> |
583 | <record model="product.cold_chain" id="cold_9"> |
584 | <field name="code">*CF</field> |
585 | + <field name="ud_code">*CF</field> |
586 | <field name="name">*CF - Problem if C and/or D blue or Freeze-tag = ALARM</field> |
587 | <field name="cold_chain" eval="True" /> |
588 | <field name="mapped_to" ref="cold_14" /> |
589 | </record> |
590 | <record model="product.cold_chain" id="cold_10"> |
591 | <field name="code">*D</field> |
592 | + <field name="ud_code">*D</field> |
593 | <field name="name">*D - Store and transport at -25°C (store in deepfreezer, transport with dry-ice)</field> |
594 | <field name="cold_chain" eval="False" /> |
595 | <field name="mapped_to" ref="cold_18" /> |
596 | </record> |
597 | <record model="product.cold_chain" id="cold_11"> |
598 | <field name="code">*F</field> |
599 | + <field name="ud_code">*F</field> |
600 | <field name="name">*F - Cannot be frozen: check FreezeWatch</field> |
601 | <field name="cold_chain" eval="False" /> |
602 | <field name="mapped_to" ref="cold_17" /> |
603 | </record> |
604 | <record model="product.cold_chain" id="cold_12"> |
605 | <field name="code">*25</field> |
606 | + <field name="ud_code">*25</field> |
607 | <field name="name">*25 - Must be kept below 25°C (but not necesseraly in cold chain)</field> |
608 | <field name="cold_chain" eval="False" /> |
609 | <field name="mapped_to" ref="cold_16" /> |
610 | </record> |
611 | <record model="product.cold_chain" id="cold_13"> |
612 | <field name="code">*25F</field> |
613 | + <field name="ud_code">*25F</field> |
614 | <field name="name">*25F - Must be kept below 25°C and cannot be frozen: check FreezeWatch</field> |
615 | <field name="cold_chain" eval="False" /> |
616 | <field name="mapped_to" ref="cold_15" /> |
617 | |
618 | === modified file 'bin/addons/product_attributes/product_attributes_view.xml' |
619 | --- bin/addons/product_attributes/product_attributes_view.xml 2024-04-16 08:38:51 +0000 |
620 | +++ bin/addons/product_attributes/product_attributes_view.xml 2024-05-13 13:47:13 +0000 |
621 | @@ -136,7 +136,7 @@ |
622 | <button name="reactivate_product" icon="gtk-execute" string="Re-activate product" type="object" attrs="{'invisible': ['|', '|', ('active', '=', True), ('nsl_merged', '=', True), ('unidata_merged', '=', True)]}" /> |
623 | <button name="open_merge_product_wizard" icon="gtk-convert" string="Merge product" type="object" attrs="{'invisible': ['|', '|', '|', ('int_status_code', '!=', 'local'), ('nsl_merged', '=', True), ('active', '=', False), ('instance_level', '!=', 'coordo')]}"/> |
624 | <button name="open_merge_hq_product_wizard" icon="gtk-convert" string="HQ Merge product" type="object" attrs="{'invisible': [('can_be_hq_merged', '!=', 'True')]}"/> |
625 | - <button name="pull_ud" icon="unidata.png" string="Pull UD OC Validation" type="object" attrs="{'invisible': ['|', ('int_status_code', '!=', 'unidata'), ('instance_level', '!=', 'section')]}" /> |
626 | + <button name="pull_ud" icon="unidata.png" string="Pull UD Product" type="object" attrs="{'invisible': ['|', ('int_status_code', '!=', 'unidata'), ('instance_level', '!=', 'section')]}" /> |
627 | <button name="debug_ud" icon="debug.png" string="Display MML API result" type="object" attrs="{'invisible': ['|', ('int_status_code', '!=', 'unidata'), ('instance_level', '!=', 'section')]}" invisible="1"/> |
628 | </group> |
629 | </group> |
630 | @@ -201,6 +201,7 @@ |
631 | <separator string="Transport" colspan="2"/> |
632 | <field name="dangerous_goods" colspan="2"/> |
633 | <field name="un_code" colspan="2"/> |
634 | + <field name="hs_code" colspan="2" attrs="{'invisible': [('int_status_code', '!=', 'unidata')]}"/> |
635 | </group> |
636 | <group colspan="2" col="2"> |
637 | <separator string="Diffusion" colspan="2"/> |
638 | @@ -1119,5 +1120,20 @@ |
639 | </field> |
640 | </record> |
641 | |
642 | + <record id="product_pull_single_ud_view" model="ir.ui.view"> |
643 | + <field name="name">product.pull_single_ud.view</field> |
644 | + <field name="model">product.pull_single_ud</field> |
645 | + <field name="type">form</field> |
646 | + <field name="arch" type="xml"> |
647 | + <form string="Pull msfid"> |
648 | + <field name="msfid" colspan="2" required="1"/> |
649 | + <group colspan="4"> |
650 | + <button name="cancel" type="object" string="Cancel" icon="gtk-cancel" /> |
651 | + <button name="pull_product" type="object" string="Pull Product" icon="gtk-go-forward" /> |
652 | + </group> |
653 | + </form> |
654 | + </field> |
655 | + </record> |
656 | + |
657 | </data> |
658 | </openerp> |
659 | |
660 | === modified file 'bin/addons/product_attributes/unidata_sync.py' |
661 | --- bin/addons/product_attributes/unidata_sync.py 2023-11-28 13:36:32 +0000 |
662 | +++ bin/addons/product_attributes/unidata_sync.py 2024-05-13 13:47:13 +0000 |
663 | @@ -1,10 +1,13 @@ |
664 | # encoding: utf-8 |
665 | from osv import fields, osv |
666 | +from osv.orm import browse_record, browse_null |
667 | from tools.translate import _ |
668 | import tools |
669 | from datetime import datetime |
670 | from dateutil import tz |
671 | import time |
672 | +import pprint |
673 | +from tools.safe_eval import safe_eval |
674 | |
675 | import logging |
676 | import logging.handlers |
677 | @@ -16,6 +19,9 @@ |
678 | |
679 | import requests |
680 | |
681 | +class UDException(Exception): |
682 | + pass |
683 | + |
684 | class unidata_country(osv.osv): |
685 | _name = 'unidata.country' |
686 | _description = 'UniData Country' |
687 | @@ -271,10 +277,13 @@ |
688 | 'end_date': fields.datetime('End Date', readonly=1), |
689 | 'number_products_pulled': fields.integer('# products pulled', readonly=1), |
690 | 'number_products_updated': fields.integer('# products updated', readonly=1), |
691 | + 'number_products_created': fields.integer('# products created', readonly=1), |
692 | + 'number_products_errors': fields.integer('# products errors', readonly=1), |
693 | + 'sync_error': fields.one2many('unidata.pull_product.log', 'session_id', 'Sync Error', readonly=1), |
694 | 'error': fields.text('Error', readonly=1), |
695 | 'page_size': fields.integer('page size', readonly=1), |
696 | 'state': fields.selection([('running', 'Running'), ('error', 'Error'), ('done', 'Done')], 'State', readonly=1), |
697 | - 'sync_type': fields.selection([('full', 'Full'), ('cont', 'Continuation'), ('diff', 'Based on last modification date')], 'Sync Type', readonly=1), |
698 | + 'sync_type': fields.selection([('full', 'Full'), ('cont', 'Continuation'), ('diff', 'Based on last modification date'),('single', 'Single MSFID')], 'Sync Type', readonly=1), |
699 | 'msfid_min': fields.integer('Min Msfid', readonly=1), |
700 | 'last_date': fields.char('Last Date', size=64, readonly=1), |
701 | 'log_file': fields.char('Path to log file', size=128, readonly=1), |
702 | @@ -284,6 +293,9 @@ |
703 | 'number_lists_pulled': fields.integer('# projects pulled', readonly=1), |
704 | } |
705 | |
706 | + _defaults = { |
707 | + } |
708 | + |
709 | unidata_sync_log() |
710 | |
711 | |
712 | @@ -300,6 +312,7 @@ |
713 | |
714 | sync_id = self.pool.get('ir.model.data').get_object_reference(self.cr, self.uid, 'product_attributes', 'unidata_sync_config')[1] |
715 | config = self.pool.get('unidata.sync').read(self.cr, self.uid, sync_id, context=self.context) |
716 | + self.unidata_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'product_attributes', 'int_6')[1] |
717 | |
718 | self.page_size = config['page_size'] or 500 |
719 | self.ud_params = { |
720 | @@ -316,10 +329,243 @@ |
721 | self.project_cache = {} |
722 | self.msf_intance_cache = {} |
723 | self.uf_instance_cache = {} |
724 | + self.uf_product_cache = {} |
725 | + self.categ_account_cache = {} |
726 | + |
727 | + self.default_oc_values = {} |
728 | + default_ids = self.pool.get('unidata.default_product_value').search(self.cr, self.uid, []) |
729 | + for default in self.pool.get('unidata.default_product_value').browse(self.cr, self.uid, default_ids): |
730 | + if default.field in ('perishable', 'batch_management'): |
731 | + if default.value == 't': |
732 | + default.value = True |
733 | + else: |
734 | + default.value = False |
735 | + |
736 | + self.default_oc_values.setdefault(default.nomenclature.id, {}).update({default.field: default.value}) |
737 | |
738 | if self.pool.get('res.company')._get_instance_level(self.cr, self.uid) != 'section': |
739 | raise osv.except_osv(_('Error'), _('UD/MSL sync can only be started at HQ level.')) |
740 | |
741 | + self.uf_config = { |
742 | + 'nomen_manda_0': { |
743 | + 'ud': 'type', |
744 | + 'relation': 'product.nomenclature', |
745 | + 'key_field': 'msfid', |
746 | + 'domain': [('level', '=', 0)], |
747 | + }, |
748 | + 'nomen_manda_1': { |
749 | + 'ud': 'group/code', |
750 | + 'relation': 'product.nomenclature', |
751 | + 'key_field': 'msfid', |
752 | + 'nomen_level': 1, |
753 | + }, |
754 | + 'nomen_manda_2': { |
755 | + 'ud': 'family/code', |
756 | + 'relation': 'product.nomenclature', |
757 | + 'key_field': 'msfid', |
758 | + 'nomen_level': 2, |
759 | + }, |
760 | + 'nomen_manda_3': { |
761 | + 'ud': 'root/code', |
762 | + 'relation': 'product.nomenclature', |
763 | + 'key_field': 'msfid', |
764 | + 'nomen_level': 3, |
765 | + }, |
766 | + 'name': { |
767 | + 'lang': { |
768 | + 'en_MF': {'ud': 'labels/english'}, |
769 | + 'fr_MF': {'ud': 'labels/french'}, |
770 | + 'es_MF': {'ud': 'labels/spanish'}, |
771 | + }, |
772 | + 'function': lambda a: a.strip(), |
773 | + }, |
774 | + 'closed_article': { |
775 | + 'ud': 'closedInfo/closed', |
776 | + 'mapping': { |
777 | + 'Open': 'no', |
778 | + 'Closed': 'yes', |
779 | + 'Restricted to': 'recommanded', |
780 | + False: 'no', |
781 | + } |
782 | + }, |
783 | + 'justification_code_id': { |
784 | + 'ud': 'supply/justification/code', |
785 | + 'relation': 'product.justification.code', |
786 | + 'key_field': 'code', |
787 | + 'ignored_values': ['SPM', 'PMFE'], # tbc with Raff |
788 | + }, |
789 | + 'controlled_substance': { |
790 | + 'ud': 'medical/controlledSubstanceGroup/controlledSubstanceInfo/code', |
791 | + 'mapping': { |
792 | + '!': '!', |
793 | + 'N1': 'N1', |
794 | + 'N2': 'N2', |
795 | + 'P1': 'P1', |
796 | + 'P2': 'P2', |
797 | + 'P3': 'P3', |
798 | + 'P4': 'P4', |
799 | + 'DP': 'DP', |
800 | + 'Y': 'Y', |
801 | + 'True': 'True', |
802 | + False: False |
803 | + } |
804 | + }, |
805 | + 'default_code': { |
806 | + 'ud': 'code' |
807 | + }, |
808 | + #'fit_value': { |
809 | + # 'lang': { |
810 | + # 'en_MF': {'ud': 'description/fitEnglishtext'}, |
811 | + # 'fr_MF': {'ud': 'description/fitFrenchtext'}, |
812 | + # } |
813 | + #}, |
814 | + #'form_value': { |
815 | + # 'lang': { |
816 | + # 'en_MF': {'ud': 'description/formEnglishtext'}, |
817 | + # 'fr_MF': {'ud': 'description/formFrenchtext'}, |
818 | + # } |
819 | + #}, |
820 | + #'function_value': { |
821 | + # 'lang': { |
822 | + # 'en_MF': {'ud': 'description/functionEnglishtext'}, |
823 | + # 'fr_MF': {'ud': 'description/functionFrenchtext'}, |
824 | + # } |
825 | + #}, |
826 | + 'cold_chain': { |
827 | + 'ud': 'supply/thermosensitiveGroup/thermosensitiveInfo/code', |
828 | + 'relation': 'product.cold_chain', |
829 | + 'key_field': 'ud_code', |
830 | + }, |
831 | + 'heat_sensitive_item': { |
832 | + 'ud': 'supply/thermosensitiveGroup/thermosensitive', |
833 | + 'relation': 'product.heat_sensitive', |
834 | + 'key_field': 'code', |
835 | + 'mapping': { |
836 | + "Don't know": 'no_know', |
837 | + False: 'no', |
838 | + 'No': 'no', |
839 | + 'Yes': 'yes', |
840 | + }, |
841 | + }, |
842 | + 'manufacturer_ref': { |
843 | + 'ud': 'closedInfo/manufacturerRef', |
844 | + }, |
845 | + 'manufacturer_txt': { |
846 | + 'ud': 'closedInfo/manufacturer', |
847 | + }, |
848 | + 'msfid': { |
849 | + 'ud': 'id', |
850 | + }, |
851 | + 'international_status': { |
852 | + 'value': self.unidata_id, |
853 | + }, |
854 | + #'name_template': tbc |
855 | + 'xmlid_code': { |
856 | + 'ud': 'id', |
857 | + 'on_update': False, |
858 | + 'function': lambda a: a and '%s'%a or False, |
859 | + }, |
860 | + 'old_code': { |
861 | + 'ud': 'formerCodes', |
862 | + 'function': lambda a: ';'.join(a), |
863 | + }, |
864 | + 'product_catalog_path': { |
865 | + 'ud': 'unicatURL', |
866 | + }, |
867 | + 'short_shelf_life': { |
868 | + 'ud': 'medical/shortShelfLifeGroup/shortShelfLife', |
869 | + 'mapping': { |
870 | + 'Yes': 'True', |
871 | + 'No': 'False', |
872 | + "Don't know": 'no_know', |
873 | + False: 'no_know', |
874 | + }, |
875 | + }, |
876 | + 'single_use': { |
877 | + 'ud': 'medical/singleUse', |
878 | + 'mapping': { |
879 | + 'Single use': 'yes', |
880 | + 'Single patient multiple use': 'yes', |
881 | + 'Reusable': 'no', |
882 | + 'Implantable Device': 'no_know', |
883 | + "Don't know": 'no_know', |
884 | + 'Not Applicable': 'no', |
885 | + False: 'no', |
886 | + } |
887 | + }, |
888 | + 'standard_ok': { |
889 | + 'ud': 'standardizationLevel', |
890 | + 'mapping': { |
891 | + 'NST': 'non_standard', |
892 | + 'STD': 'standard', |
893 | + 'NSL': 'non_standard_local', |
894 | + } |
895 | + }, |
896 | + 'standard_price': { |
897 | + 'value': 1.0, |
898 | + 'on_update': False, |
899 | + }, |
900 | + 'state_ud': { |
901 | + 'ud': 'lifeCycleStatus', |
902 | + 'mapping': { |
903 | + '01. Preparation': 'valid', |
904 | + '02. Valid': 'valid', |
905 | + '03. Outdated': 'outdated', |
906 | + '04. Discontinued': 'discontinued', |
907 | + '05. Forbidden': 'forbidden', |
908 | + '06. Rejected': 'stopped', |
909 | + '08. Archived': 'archived', |
910 | + '01. Temporary Golden:': 'stopped', |
911 | + '01. Temporary Merge': 'stopped', |
912 | + '07. Parked': 'archived', |
913 | + }, |
914 | + }, |
915 | + 'sterilized': { |
916 | + 'ud': 'medical/sterile', |
917 | + 'mapping': { |
918 | + 'Yes': 'yes', |
919 | + 'No': 'no', |
920 | + "Don't know": 'no_know', |
921 | + False: 'no_know', |
922 | + } |
923 | + }, |
924 | + 'dangerous_goods': { |
925 | + 'ud': 'supply/dangerousGroup/dangerous', |
926 | + 'mapping': { |
927 | + "Don't know": 'no_know', |
928 | + 'Yes': 'True', |
929 | + 'No': 'False', |
930 | + False: 'False', |
931 | + } |
932 | + }, |
933 | + 'un_code': { |
934 | + 'ud': 'supply/dangerousGroup/dangerousInfo/number', |
935 | + }, |
936 | + 'hs_code': { |
937 | + 'ud': 'supply/hsCode', |
938 | + }, |
939 | + 'oc_subscription': { |
940 | + 'ud': 'ocSubscriptions/%s' % self.oc, |
941 | + }, |
942 | + 'oc_validation': { |
943 | + 'ud': 'ocValidations/%s/valid' % self.oc, |
944 | + }, |
945 | + 'oc_validation_date': { |
946 | + 'ud': 'ocValidations/%s/lastValidationDate' % self.oc, |
947 | + 'type': 'date', |
948 | + }, |
949 | + 'oc_devalidation_date': { |
950 | + 'ud': 'ocValidations/%s/lastDevalidationDate' % self.oc, |
951 | + 'type': 'date', |
952 | + }, |
953 | + 'oc_devalidation_reason': { |
954 | + 'ud': 'ocValidations/%s/devalidationReason' % self.oc, |
955 | + }, |
956 | + 'oc_comments': { |
957 | + 'ud': 'ocValidations/%s/comments' % self.oc, |
958 | + } |
959 | + } |
960 | + |
961 | def create_msl_list(self): |
962 | oc_number = { |
963 | 'oca': 5, |
964 | @@ -524,15 +770,165 @@ |
965 | llevel = logging.INFO |
966 | self.logger.log(llevel, msg) |
967 | |
968 | - def update_products(self, q_filter, record_date): |
969 | + def map_ud_fields(self, ud_data, new_prod): |
970 | + uf_values = {'en_MF': {}} |
971 | + for uf_key in self.uf_config: |
972 | + for lang in self.uf_config[uf_key].get('lang', ['default']): |
973 | + if lang == 'default': |
974 | + lang_values = uf_values['en_MF'] |
975 | + field_desc = self.uf_config[uf_key] |
976 | + else: |
977 | + lang_values = uf_values.get(lang, {}) |
978 | + field_desc = self.uf_config[uf_key]['lang'][lang] |
979 | + |
980 | + if field_desc.get('value'): |
981 | + if new_prod or field_desc.get('on_update', True): |
982 | + lang_values[uf_key] = field_desc['value'] |
983 | + continue |
984 | + |
985 | + uf_value = ud_data |
986 | + for key in field_desc['ud'].split('/'): |
987 | + uf_value = uf_value.get(key, {}) |
988 | + if not uf_value: |
989 | + uf_value = False |
990 | + |
991 | + if field_desc.get('mapping'): |
992 | + if uf_value not in field_desc['mapping']: |
993 | + raise UDException('Mapping error, uf_key: %s, uf_value: %s' % (uf_key, uf_value)) |
994 | + else: |
995 | + uf_value = field_desc['mapping'][uf_value] |
996 | + |
997 | + if 'ignored_values' in field_desc and uf_value in field_desc.get('ignored_values'): |
998 | + uf_value = False |
999 | + |
1000 | + if uf_key in ['nomen_manda_1', 'nomen_manda_2', 'nomen_manda_3']: |
1001 | + previous_nom = { |
1002 | + 'nomen_manda_1': 'nomen_manda_0', |
1003 | + 'nomen_manda_2': 'nomen_manda_1', |
1004 | + 'nomen_manda_3': 'nomen_manda_2', |
1005 | + }[uf_key] |
1006 | + self.uf_product_cache.setdefault(uf_key, {}) |
1007 | + |
1008 | + if uf_key == 'nomen_manda_1': |
1009 | + msfid = '%s-%s' % (ud_data['type'], ud_data['group']['code']) |
1010 | + ud_key = 'group' |
1011 | + elif uf_key == 'nomen_manda_2': |
1012 | + msfid = '%s-%s-%s%s' % (ud_data['type'], ud_data['group']['code'], ud_data['group']['code'], ud_data['family']['code']) |
1013 | + ud_key = 'family' |
1014 | + else: |
1015 | + msfid = '%s-%s-%s%s-%s' % (ud_data['type'], ud_data['group']['code'], ud_data['group']['code'], ud_data['family']['code'], ud_data['root']['code']) |
1016 | + ud_key = 'root' |
1017 | + |
1018 | + #if previous_nom not in lang_values: |
1019 | + # continue |
1020 | + cache_key = (uf_key, lang_values[previous_nom], msfid) |
1021 | + |
1022 | + if cache_key not in self.uf_product_cache[uf_key]: |
1023 | + domain = [('level', '=', self.uf_config[uf_key]['nomen_level']), ('parent_id', '=', uf_values['en_MF'][previous_nom]), ('msfid', '=', msfid)] |
1024 | + nomen_id = self.pool.get('product.nomenclature').search(self.cr, self.uid, domain, context=self.context) |
1025 | + created_nomen = False |
1026 | + if not nomen_id: |
1027 | + created_nomen = True |
1028 | + self.log('==== create nomenclature %s'%msfid) |
1029 | + nomen_data = { |
1030 | + 'name': ud_data[ud_key]['labels']['english'], |
1031 | + 'msfid': msfid, |
1032 | + 'parent_id': uf_values['en_MF'][previous_nom], |
1033 | + 'level': self.uf_config[uf_key]['nomen_level'], |
1034 | + } |
1035 | + nomen_id = [self.pool.get('product.nomenclature').create(self.cr, self.uid, nomen_data, context={'lang': 'en_MF'})] |
1036 | + if ud_data.get(ud_key, {}).get('labels', {}).get('french'): |
1037 | + self.pool.get('product.nomenclature').write(self.cr, self.uid, nomen_id, {'name': ud_data[ud_key]['labels']['french']}, context={'lang': 'fr_MF'}) |
1038 | + if uf_key == 'nomen_manda_2': |
1039 | + self.log('===== create category %s'%msfid) |
1040 | + account_ids = self.pool.get('account.account').search(self.cr, self.uid, [('type', '!=', 'view'), ('code', '=', ud_data.get('accountCode', {}).get('code'))]) |
1041 | + if not account_ids: |
1042 | + raise UDException('Account code %s not found' % (ud_data.get('accountCode', {}).get('code'))) |
1043 | + account_id = account_ids[0] |
1044 | + categ_id = self.pool.get('product.category').create(self.cr, self.uid, { |
1045 | + 'name': ud_data['family']['labels']['english'], |
1046 | + 'msfid': msfid, |
1047 | + 'family_id': nomen_id[0], |
1048 | + 'property_account_income_categ': account_id, |
1049 | + 'property_account_expense_categ': account_id, |
1050 | + }, context={'lang': 'en_MF'}) |
1051 | + if ud_data.get('family', {}).get('labels', {}).get('french'): |
1052 | + self.pool.get('product.category').write(self.cr, self.uid, categ_id, {'name': ud_data['family']['labels']['french']}, context={'lang': 'fr_MF'}) |
1053 | + else: |
1054 | + self.uf_product_cache[uf_key][cache_key] = nomen_id |
1055 | + else: |
1056 | + nomen_id = self.uf_product_cache[uf_key][cache_key] |
1057 | + if not nomen_id or len(nomen_id) != 1: |
1058 | + raise UDException('%s error %s not found %s' % (uf_key, cache_key, nomen_id)) |
1059 | + if False and uf_key == 'nomen_manda_2': |
1060 | + if msfid not in self.categ_account_cache: |
1061 | + if not created_nomen: |
1062 | + self.categ_account_cache[msfid] = self.pool.get('product.nomenclature').browse(self.cr, self.uid, nomen_id[0]).category_id.property_account_income_categ.code |
1063 | + categ_account_code = self.categ_account_cache[msfid] |
1064 | + else: |
1065 | + categ_account_code = ud_data.get('accountCode', {}).get('code') |
1066 | + |
1067 | + else: |
1068 | + categ_account_code = self.categ_account_cache[msfid] |
1069 | + |
1070 | + if ud_data.get('accountCode', {}).get('code') != categ_account_code: |
1071 | + account_ids = self.pool.get('account.account').search(self.cr, self.uid, [('type', '!=', 'view'), ('code', '=', ud_data.get('accountCode', {}).get('code'))]) |
1072 | + if not account_ids: |
1073 | + raise UDException('Account code %s not found' % (ud_data.get('accountCode', {}).get('code'))) |
1074 | + account_id = account_ids[0] |
1075 | + else: |
1076 | + account_id = False |
1077 | + lang_values['property_account_income'] = account_id |
1078 | + lang_values['property_account_expense'] = account_id |
1079 | + |
1080 | + lang_values[uf_key] = nomen_id[0] |
1081 | + |
1082 | + elif field_desc.get('relation'): |
1083 | + self.uf_product_cache.setdefault(uf_key, {}) |
1084 | + if not uf_value: |
1085 | + self.uf_product_cache[uf_key][uf_value] = [False] |
1086 | + if uf_value not in self.uf_product_cache[uf_key]: |
1087 | + domain = [(field_desc['key_field'], '=', uf_value)] |
1088 | + if field_desc.get('domain'): |
1089 | + domain += field_desc['domain'] |
1090 | + self.uf_product_cache[uf_key][uf_value] = self.pool.get(field_desc['relation']).search(self.cr, self.uid, domain, context=self.context) |
1091 | + if not self.uf_product_cache[uf_key][uf_value] or len(self.uf_product_cache[uf_key][uf_value]) > 1: |
1092 | + raise UDException('Field error %s: uf_value: %s, records found in UF: %d, %s' % (uf_key, uf_value, len(self.uf_product_cache[uf_key][uf_value]), self.uf_product_cache[uf_key][uf_value])) |
1093 | + else: |
1094 | + if new_prod or field_desc.get('on_update', True): |
1095 | + lang_values[uf_key] = self.uf_product_cache[uf_key][uf_value][0] |
1096 | + elif field_desc.get('function'): |
1097 | + if new_prod or field_desc.get('on_update', True): |
1098 | + lang_values[uf_key] = field_desc['function'](uf_value) |
1099 | + else: |
1100 | + if uf_value and field_desc.get('type') == 'date': |
1101 | + uf_value = self.ud_date(uf_value) |
1102 | + |
1103 | + if new_prod or field_desc.get('on_update', True): |
1104 | + lang_values[uf_key] = uf_value |
1105 | + if new_prod: |
1106 | + for nomen in ['nomen_manda_0', 'nomen_manda_1', 'nomen_manda_2', 'nomen_manda_3']: |
1107 | + if uf_values['en_MF'].get(nomen) and uf_values['en_MF'][nomen] in self.default_oc_values: |
1108 | + uf_values['en_MF'].update(self.default_oc_values[uf_values['en_MF'][nomen]]) |
1109 | + |
1110 | + if uf_values['en_MF'].get('batch_management'): |
1111 | + uf_values['en_MF']['perishable'] = True |
1112 | + |
1113 | + return uf_values |
1114 | + |
1115 | + def update_products(self, q_filter, record_date, session_id=False): |
1116 | country_obj = self.pool.get('unidata.country') |
1117 | project_obj = self.pool.get('unidata.project') |
1118 | prod_obj = self.pool.get('product.product') |
1119 | + pull_log = self.pool.get('unidata.pull_product.log') |
1120 | + |
1121 | |
1122 | page = 1 |
1123 | date_to_record = False |
1124 | prod_updated = 0 |
1125 | rows_seen = 0 |
1126 | + nb_errors = 0 |
1127 | + nb_created = 0 |
1128 | while True: |
1129 | js = self.query(q_filter, page=page) |
1130 | |
1131 | @@ -546,65 +942,171 @@ |
1132 | for x in js.get('rows'): |
1133 | self.log('UD: %s' % x) |
1134 | rows_seen += 1 |
1135 | - prod_id = prod_obj.search(self.cr, self.uid, [('msfid', '=', x['id']), ('active', 'in', ['t', 'f'])], order='active desc, id', context=self.context) |
1136 | - if not prod_id: |
1137 | - self.log('Product not found in UF, msfid: %s, code: %s' % (x['id'], x['code'], ), 'warn') |
1138 | - continue |
1139 | - |
1140 | - oc_data = x.get('ocValidations', {}).get(self.oc, {}) |
1141 | - data = { |
1142 | - 'oc_validation': oc_data.get('valid'), |
1143 | - 'oc_validation_date': False, |
1144 | - 'oc_devalidation_date': False, |
1145 | - 'oc_devalidation_reason': oc_data.get('devalidationReason'), |
1146 | - 'oc_comments': oc_data.get('comments'), |
1147 | - |
1148 | - } |
1149 | - |
1150 | - if oc_data.get('lastValidationDate'): |
1151 | - data['oc_validation_date'] = self.ud_date(oc_data['lastValidationDate']) |
1152 | - if oc_data.get('lastDevalidationDate'): |
1153 | - data['oc_devalidation_date'] = self.ud_date(oc_data['lastDevalidationDate']) |
1154 | - |
1155 | - c_restriction = [] |
1156 | - p_restriction = [] |
1157 | - for mr in oc_data.get('missionRestrictions', []): |
1158 | - if mr.get('country', {}).get('labels', {}).get('english'): |
1159 | - if mr['country']['labels']['english'] not in self.country_cache: |
1160 | - c_id = country_obj.search(self.cr, self.uid, [('name', '=', mr['country']['labels']['english'])], context=self.context) |
1161 | - if not c_id: |
1162 | - c_id = country_obj.create(self.cr, self.uid, {'name': mr['country']['labels']['english']}, context=self.context) |
1163 | - |
1164 | - self.log('Create country %s' % (mr['country']['labels']['english'],)) |
1165 | - self.country_cache[mr['country']['labels']['english']] = c_id |
1166 | - else: |
1167 | - self.country_cache[mr['country']['labels']['english']] = c_id[0] |
1168 | - c_restriction.append(self.country_cache[mr['country']['labels']['english']]) |
1169 | - |
1170 | - for pr in mr.get('projectRestrictions', []): |
1171 | - # TODO create UF instance |
1172 | - if pr.get('code'): |
1173 | - if pr.get('code') not in self.project_cache: |
1174 | - p_id = project_obj.search(self.cr, self.uid, [('code', '=', pr['code'])], context=self.context) |
1175 | - if not p_id: |
1176 | - self.log('Create project %s' % (pr['code'], )) |
1177 | - self.project_cache[pr['code']] = project_obj.create(self.cr, self.uid, {'code': pr['code'], 'name': pr.get('name')}, context=self.context) |
1178 | + try: |
1179 | + self.cr.execute("SAVEPOINT nom_ud_update") |
1180 | + #prod_id = prod_obj.search(self.cr, self.uid, [('msfid', '=', x['id']), ('active', 'in', ['t', 'f'])], order='active desc, id', context=self.context) |
1181 | + #if x.get('state') == 'Golden': |
1182 | + # continue |
1183 | + prod_ids = [] |
1184 | + if not x.get('id'): |
1185 | + raise UDException('No msfid in API') |
1186 | + |
1187 | + if not x.get('formerCodes'): |
1188 | + raise UDException('No formerCodes code') |
1189 | + if not x.get('type') or not x.get('group', {}).get('code') or not x.get('family', {}).get('code') or not x.get('root', {}).get('code'): |
1190 | + raise UDException('Nomenclature not set in UD') |
1191 | + |
1192 | + prod_ids = prod_obj.search(self.cr, self.uid, [('msfid', '=', x.get('id')), ('active', 'in', ['t', 'f']), ('international_status', '=', self.unidata_id)], context=self.context) |
1193 | + if len(prod_ids) > 1: |
1194 | + raise UDException('%s products found for msfid %s: %s' % (len(prod_ids), x.get('id'), prod_obj.read(self.cr, self.uid, prod_ids, ['default_code']))) |
1195 | + |
1196 | + if len(prod_ids) == 0: |
1197 | + if not x.get('ocSubscriptions').get(self.oc): |
1198 | + self.log('%s product ignored: ocSubscriptions False' % x['formerCodes']) |
1199 | + continue |
1200 | + self.log('%s product to create' % (x['formerCodes'], )) |
1201 | + else: |
1202 | + if not x.get('ocSubscriptions').get(self.oc): |
1203 | + if not prod_obj.search(self.cr, self.uid, [('id', 'in', prod_ids), ('oc_subscription', '=', True), ('active', 'in', ['t', 'f'])], context=self.context): |
1204 | + self.log('%s product ignored: ocSubscriptions False in UD and UF' % x['code']) |
1205 | + continue |
1206 | + |
1207 | + self.log('%s product found %s' % (x['formerCodes'], prod_ids[0])) |
1208 | + |
1209 | + product_values = self.map_ud_fields(x, new_prod=not bool(prod_ids)) |
1210 | + |
1211 | + c_restriction = [] |
1212 | + p_restriction = [] |
1213 | + oc_data = x.get('ocValidations', {}).get(self.oc, {}) |
1214 | + for mr in oc_data.get('missionRestrictions', []): |
1215 | + if mr.get('country', {}).get('labels', {}).get('english'): |
1216 | + if mr['country']['labels']['english'] not in self.country_cache: |
1217 | + c_id = country_obj.search(self.cr, self.uid, [('name', '=', mr['country']['labels']['english'])], context=self.context) |
1218 | + if not c_id: |
1219 | + c_id = country_obj.create(self.cr, self.uid, {'name': mr['country']['labels']['english']}, context=self.context) |
1220 | + |
1221 | + self.log('Create country %s' % (mr['country']['labels']['english'],)) |
1222 | + self.country_cache[mr['country']['labels']['english']] = c_id |
1223 | else: |
1224 | - self.project_cache[pr['code']] = p_id[0] |
1225 | - p_restriction.append(self.project_cache[pr['code']]) |
1226 | - |
1227 | - data.update({ |
1228 | - 'oc_country_restrictions': [(6, 0, list(set(c_restriction)))], |
1229 | - 'oc_project_restrictions': [(6, 0, list(set(p_restriction)))], |
1230 | - }) |
1231 | - self.log('Write product id: %d, code: %s, msfid: %s, data: %s' % (prod_id[0], x['code'], x['id'], data)) |
1232 | - prod_obj.write(self.cr, self.uid, prod_id[0], data, context=self.context) |
1233 | - prod_updated += 1 |
1234 | + self.country_cache[mr['country']['labels']['english']] = c_id[0] |
1235 | + c_restriction.append(self.country_cache[mr['country']['labels']['english']]) |
1236 | + |
1237 | + for pr in mr.get('projectRestrictions', []): |
1238 | + # TODO create UF instance |
1239 | + if pr.get('code'): |
1240 | + if pr.get('code') not in self.project_cache: |
1241 | + p_id = project_obj.search(self.cr, self.uid, [('code', '=', pr['code'])], context=self.context) |
1242 | + if not p_id: |
1243 | + self.log('Create project %s' % (pr['code'], )) |
1244 | + self.project_cache[pr['code']] = project_obj.create(self.cr, self.uid, {'code': pr['code'], 'name': pr.get('name')}, context=self.context) |
1245 | + else: |
1246 | + self.project_cache[pr['code']] = p_id[0] |
1247 | + p_restriction.append(self.project_cache[pr['code']]) |
1248 | + |
1249 | + product_values['en_MF'].update({ |
1250 | + 'oc_country_restrictions': [(6, 0, list(set(c_restriction)))], |
1251 | + 'oc_project_restrictions': [(6, 0, list(set(p_restriction)))], |
1252 | + }) |
1253 | + |
1254 | + |
1255 | + if prod_ids: |
1256 | + diff = [] |
1257 | + for lang in ['en_MF', 'fr_MF', 'sp_MF']: |
1258 | + if not product_values.get(lang): |
1259 | + continue |
1260 | + current_value = prod_obj.browse(self.cr, self.uid, prod_ids[0], fields_to_fetch=product_values[lang].keys(), context={'lang': lang}) |
1261 | + for key, value in product_values[lang].items(): |
1262 | + tmp_diff = False |
1263 | + if key in ('oc_country_restrictions', 'oc_project_restrictions'): |
1264 | + if set(value[0][2]) != set([x.id for x in current_value[key]]): |
1265 | + self.log('Field diff %s, uf: *%s*, ud: *%s*'% (key, [x.id for x in current_value[key]], value[0][2])) |
1266 | + tmp_diff = True |
1267 | + elif isinstance(current_value[key], browse_record): |
1268 | + if current_value[key]['id'] != value: |
1269 | + self.log('Field diff m2o empty %s, uf: *%s*, ud: *%s*'% (key, current_value[key]['id'], value)) |
1270 | + tmp_diff = True |
1271 | + elif isinstance(current_value[key], browse_null): |
1272 | + if value: |
1273 | + self.log('Field diff m2o %s, uf: *%s*, ud: *%s*'% (key, current_value[key]['id'], value)) |
1274 | + tmp_diff = True |
1275 | + elif current_value[key] != value and ( value is False and current_value[key] or value is not False): |
1276 | + self.log('Field diff %s, uf: *%s*, ud: *%s*'% (key, current_value[key], value)) |
1277 | + tmp_diff = True |
1278 | + if tmp_diff: |
1279 | + diff.append(key) |
1280 | + if diff: |
1281 | + # not not check fr/sp |
1282 | + break |
1283 | + |
1284 | + if not diff: |
1285 | + self.log('==== same values %s %s' % (product_values.get('en_MF', {}).get('default_code'), prod_ids[0])) |
1286 | + continue |
1287 | + else: |
1288 | + self.log('==== diff values %s %s, key: %s' % (product_values.get('en_MF', {}).get('default_code'), prod_ids[0], diff)) |
1289 | + |
1290 | + try: |
1291 | + self.cr.execute("SAVEPOINT prod_ud_update") |
1292 | + if prod_ids: |
1293 | + self.log('==== write product id: %d, code: %s, msfid: %s, data: %s' % (prod_ids[0], x['code'], x['id'], product_values['en_MF'])) |
1294 | + prod_obj.write(self.cr, self.uid, prod_ids[0], product_values['en_MF'], context={'lang': 'en_MF'}) |
1295 | + prod_updated += 1 |
1296 | + else: |
1297 | + prod_ids = [prod_obj.create(self.cr, self.uid, product_values['en_MF'], context={'lang': 'en_MF'})] |
1298 | + self.log('==== create product id: %d, code: %s, msfid: %s, data: %s' % (prod_ids[0], x['code'], x['id'], product_values['en_MF'])) |
1299 | + nb_created += 1 |
1300 | + |
1301 | + for lang in ['fr_MF', 'sp_MF']: |
1302 | + if product_values.get(lang): |
1303 | + prod_obj.write(self.cr, self.uid, prod_ids[0], product_values['lang'], context={'lang': 'lang'}) |
1304 | + except Exception as e: |
1305 | + self.cr.execute("ROLLBACK TO SAVEPOINT prod_ud_update") |
1306 | + raise UDException(tools.misc.get_traceback(e)) |
1307 | + else: |
1308 | + self.cr.execute("RELEASE SAVEPOINT prod_ud_update") |
1309 | + |
1310 | + except UDException as e: |
1311 | + self.cr.execute("ROLLBACK TO SAVEPOINT nom_ud_update") |
1312 | + self.log('ERROR %s'% e.args[0]) |
1313 | + if session_id: |
1314 | + data_log = { |
1315 | + 'msfid': x.get('id', ''), |
1316 | + 'code': x.get('code', ''), |
1317 | + 'former_codes': x.get('formerCodes', ''), |
1318 | + 'log': e.args[0], |
1319 | + 'json_data': x, |
1320 | + 'session_id': session_id, |
1321 | + } |
1322 | + to_write = [] |
1323 | + if x.get('id', ''): |
1324 | + to_write = pull_log.search(self.cr, self.uid,[('msfid', '=', x.get('id', '')), ('session_id', '=', session_id)]) |
1325 | + |
1326 | + if to_write: |
1327 | + pull_log.write(self.cr, self.uid, to_write, data_log) |
1328 | + else: |
1329 | + pull_log.create(self.cr, self.uid, data_log) |
1330 | + nb_errors += 1 |
1331 | + else: |
1332 | + nb_errors += 1 |
1333 | + if x.get('id', ''): |
1334 | + self.cr.execute('''insert into unidata_products_error (msfid, code, former_codes, date, log, uf_product_id, json_data) |
1335 | + values (%(msfid)s, %(code)s, %(former_codes)s, NOW(), %(log)s, %(uf_product_id)s, %(json_data)s) |
1336 | + on conflict (msfid) do update SET code = %(code)s, former_codes=%(former_codes)s, date=NOW(), log=%(log)s, uf_product_id=%(uf_product_id)s, json_data=%(json_data)s, fixed_date=NULL |
1337 | + ''', { |
1338 | + 'msfid': x.get('id'), |
1339 | + 'code': x.get('code', ''), |
1340 | + 'former_codes': '%s' % x.get('formerCodes', ''), |
1341 | + 'log': e.args[0], |
1342 | + 'uf_product_id': ','.join([str(x) for x in prod_ids]), |
1343 | + 'json_data': '%s'%x, |
1344 | + }) |
1345 | + else: |
1346 | + self.cr.execute("RELEASE SAVEPOINT nom_ud_update") |
1347 | + |
1348 | page += 1 |
1349 | if len(js.get('rows')) < self.page_size: |
1350 | break |
1351 | |
1352 | - return date_to_record, rows_seen, prod_updated |
1353 | + return date_to_record, rows_seen, prod_updated, nb_created, nb_errors |
1354 | |
1355 | |
1356 | |
1357 | @@ -622,7 +1124,7 @@ |
1358 | def _get_log(self, cr, uid, ids, field_name, args, context=None): |
1359 | res = {} |
1360 | |
1361 | - cr.execute("select start_date, end_date, state, sync_type from unidata_sync_log where server='ud' order by id desc limit 1") |
1362 | + cr.execute("select start_date, end_date, state, sync_type from unidata_sync_log where server='ud' and coalesce(sync_type, '')!='single' order by id desc limit 1") |
1363 | one = cr.fetchone() |
1364 | if one: |
1365 | last_execution_start_date = one[0] |
1366 | @@ -640,7 +1142,6 @@ |
1367 | last_msl_execution_status = one[2] |
1368 | last_msl_execution_sync_type = one[3] |
1369 | else: |
1370 | - last_execution_start_date = last_execution_end_date = last_execution_status = last_execution_sync_type = False |
1371 | last_msl_execution_start_date = last_msl_execution_end_date = last_msl_execution_status = last_msl_execution_sync_type = False |
1372 | |
1373 | param_obj = self.pool.get('ir.config_parameter') |
1374 | @@ -661,7 +1162,6 @@ |
1375 | 'last_msl_execution_sync_type': last_msl_execution_sync_type, |
1376 | 'eligible_msl_full_sync': eligible_msl_full_sync, |
1377 | } |
1378 | - |
1379 | return res |
1380 | |
1381 | def _get_next_planned_date(self, cr, uid, ids, field_name, args, context=None): |
1382 | @@ -849,7 +1349,7 @@ |
1383 | new_thread.start() |
1384 | new_thread.join(3.0) |
1385 | if not new_thread.is_alive() and self._error: |
1386 | - raise self._error |
1387 | + raise Exception(self._error) |
1388 | return True |
1389 | |
1390 | def start_ud_sync(self, cr, uid, context=None): |
1391 | @@ -878,6 +1378,8 @@ |
1392 | |
1393 | nb_prod = 0 |
1394 | updated = 0 |
1395 | + total_nb_errors = 0 |
1396 | + total_nb_created = 0 |
1397 | |
1398 | if full: |
1399 | param_obj.set_param(cr, 1, 'LAST_UD_DATE_SYNC', '') |
1400 | @@ -889,7 +1391,8 @@ |
1401 | sync_type = 'diff' |
1402 | |
1403 | |
1404 | - logger = logging.getLogger('unidata-sync') |
1405 | + oc = self.pool.get('sync.client.entity').get_entity(cr, uid, context).oc |
1406 | + logger = logging.getLogger('unidata-sync-%s'% oc) |
1407 | sync_obj = ud_sync(cr, uid, self.pool, logger=logger, context=context) |
1408 | page_size = sync_obj.page_size |
1409 | |
1410 | @@ -926,15 +1429,32 @@ |
1411 | logger.info('Sync start, page size: %s, last msfid: %s, last date: %s' % (page_size, min_msfid, last_ud_date_sync)) |
1412 | |
1413 | try: |
1414 | - while True: |
1415 | + last_loop = False |
1416 | + max_id = 0 |
1417 | + # first tries previous errors |
1418 | + cr.execute('select distinct(msfid) from unidata_products_error where fixed_date is null') |
1419 | + query = [] |
1420 | + all_msfids = [x[0] for x in cr.fetchall()] |
1421 | + for x in all_msfids: |
1422 | + query.append("msfIdentifier=%s" % x) |
1423 | + if query: |
1424 | + cr.execute('update unidata_products_error set fixed_date=NOW() where msfid in %s', (tuple(all_msfids),)) |
1425 | + trash1, nb_prod, updated, total_nb_created, total_nb_errors = sync_obj.update_products(" or ".join(query), False, session_id) |
1426 | + while not last_loop: |
1427 | cr.execute('SAVEPOINT unidata_sync_log') |
1428 | cr.execute("select min(msfid), max(msfid) from product_product p where id in (select id from product_product where coalesce(msfid,0)!=0 and msfid>%s order by msfid limit %s)", (min_msfid, page_size)) |
1429 | min_id, max_id = cr.fetchone() |
1430 | min_msfid = max_id |
1431 | if not min_id: |
1432 | - break |
1433 | + last_loop = True |
1434 | + cr.execute("select max(msfid) from product_product p") |
1435 | + min_msfid = cr.fetchone()[0] or 0 |
1436 | + q_filter = "(msfIdentifier>=%s)" % min_msfid |
1437 | + else: |
1438 | + if first_query: |
1439 | + min_id = 0 |
1440 | + q_filter = "(msfIdentifier>=%s and msfIdentifier<=%s)"%(min_id, max_id) |
1441 | |
1442 | - q_filter = "(msfIdentifier>=%s and msfIdentifier<=%s)"%(min_id, max_id) |
1443 | if last_ud_date_sync: |
1444 | lastsync = (datetime.strptime(last_ud_date_sync.split('T')[0], '%Y-%m-%d') + relativedelta(hours=-24)).strftime('%Y-%m-%dT00:00:00') |
1445 | createdOn = (datetime.strptime(last_ud_date_sync.split('T')[0], '%Y-%m-%d') + relativedelta(days=-3)).strftime('%Y-%m-%dT00:00:00') |
1446 | @@ -944,7 +1464,7 @@ |
1447 | 'createdOn': createdOn, |
1448 | } |
1449 | |
1450 | - s_date_to_record, rows_seen, prod_updated = sync_obj.update_products(q_filter, first_query) |
1451 | + s_date_to_record, rows_seen, prod_updated, nb_created, nb_errors = sync_obj.update_products(q_filter, first_query, session_id) |
1452 | if first_query and s_date_to_record: |
1453 | logger.info('Set last date: %s', s_date_to_record) |
1454 | param_obj.set_param(cr, 1, 'LAST_UD_DATE_SYNC', s_date_to_record) |
1455 | @@ -953,8 +1473,10 @@ |
1456 | |
1457 | updated += prod_updated |
1458 | nb_prod += rows_seen |
1459 | + total_nb_errors += nb_errors |
1460 | + total_nb_created += nb_created |
1461 | |
1462 | - session_obj.write(cr, uid, session_id, {'number_products_pulled': nb_prod, 'number_products_updated': updated}, context=context) |
1463 | + session_obj.write(cr, uid, session_id, {'number_products_pulled': nb_prod, 'number_products_updated': updated, 'number_products_created': total_nb_created, 'number_products_errors': total_nb_errors}, context=context) |
1464 | cr.commit() |
1465 | |
1466 | # end of sql loop |
1467 | @@ -965,13 +1487,13 @@ |
1468 | logger.error('End of Script with error: %s' % error) |
1469 | handler.close() |
1470 | logger.removeHandler(handler) |
1471 | - session_obj.write(cr, uid, session_id, {'end_date': fields.datetime.now(), 'state': 'error', 'number_products_pulled': nb_prod, 'error': error, 'number_products_updated': updated}, context=context) |
1472 | + session_obj.write(cr, uid, session_id, {'end_date': fields.datetime.now(), 'state': 'error', 'number_products_pulled': nb_prod, 'error': error, 'number_products_updated': updated, 'number_products_created': total_nb_created, 'number_products_errors': total_nb_errors}, context=context) |
1473 | return False |
1474 | |
1475 | logger.info('End of Script') |
1476 | handler.close() |
1477 | logger.removeHandler(handler) |
1478 | - session_obj.write(cr, uid, session_id, {'end_date': fields.datetime.now(), 'state': 'done', 'number_products_pulled': nb_prod, 'number_products_updated': updated}, context=context) |
1479 | + session_obj.write(cr, uid, session_id, {'end_date': fields.datetime.now(), 'state': 'done', 'number_products_pulled': nb_prod, 'number_products_updated': updated, 'number_products_created': total_nb_created, 'number_products_errors': total_nb_errors}, context=context) |
1480 | param_obj.set_param(cr, 1, 'LAST_MSFID_SYNC', '') |
1481 | return True |
1482 | |
1483 | @@ -988,4 +1510,127 @@ |
1484 | |
1485 | unidata_sync() |
1486 | |
1487 | - |
1488 | +class unidata_default_product_value(osv.osv): |
1489 | + _name = 'unidata.default_product_value' |
1490 | + _description = 'OC Default values' |
1491 | + |
1492 | + def _get_number_incompatible_products(self, cr, uid, ids, field_name, args, context=None): |
1493 | + res = {} |
1494 | + ud_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'product_attributes', 'int_6')[1] |
1495 | + prod_obj = self.pool.get('product.product') |
1496 | + for oc_val in self.browse(cr, uid, ids, fields_to_fetch=['nomenclature', 'value', 'field'], context=context): |
1497 | + value = oc_val.value |
1498 | + if value == 'f': |
1499 | + value = False |
1500 | + res[oc_val.id] = prod_obj.search(cr, uid, [('international_status', '=', ud_id), ('nomen_manda_%d' % oc_val.nomenclature.level, '=', oc_val.nomenclature.id), (oc_val.field, '!=', value)], count=True, context=context) |
1501 | + |
1502 | + return res |
1503 | + |
1504 | + _columns = { |
1505 | + 'field': fields.selection([('perishable', 'Expiry Date Mandatory'), ('batch_management', 'Batch Number Mandatory'), ('procure_method','Procurement Method'), ('type', 'Product Type'), ('subtype', 'Product SubType')], 'Field Name', required=1, select=1), |
1506 | + 'value': fields.char('Value', size=256, required=1, select=1), |
1507 | + 'nomenclature': fields.many2one('product.nomenclature', required=1, string="Nomenclature"), |
1508 | + 'number_incompatible_products': fields.function(_get_number_incompatible_products, method=True, type='integer', string='Nb inconsistent prod'), |
1509 | + } |
1510 | + |
1511 | + _sql_constraints = [ |
1512 | + ('unique_nomenclature_field', 'unique(field, nomenclature)', 'Field / nomenclature already exists') |
1513 | + ] |
1514 | + |
1515 | + def _check_value(self, cr, uid, ids, context=None): |
1516 | + if not ids: |
1517 | + return True |
1518 | + for oc in self.browse(cr, uid, ids, context=context): |
1519 | + if oc.field in ['perishable', 'batch_management'] and oc.value not in ['t', 'f']: |
1520 | + raise osv.except_osv(_('Error'), _('Expiry Date Mandatory and Batch Number Mandatory: only t or f are allowed')) |
1521 | + if oc.field == 'procure_method' and oc.value not in ['make_to_stock', 'make_to_order']: |
1522 | + raise osv.except_osv(_('Error'), _('Procurement Method: only make_to_stock or make_to_order are allowed')) |
1523 | + if oc.field == 'type' and oc.value not in ['product', 'consu', 'service_recep']: |
1524 | + raise osv.except_osv(_('Error'), _('Product Type only product, consu or service_recep are allowed')) |
1525 | + if oc.field == 'subtype' and oc.value not in ['single', 'kit', 'asset', '']: |
1526 | + raise osv.except_osv(_('Error'), _("Product SubType only single, kit, asset or '' are allowed")) |
1527 | + |
1528 | + return True |
1529 | + |
1530 | + |
1531 | + _constraints = [ |
1532 | + (_check_value, 'Value not allowed', []) |
1533 | + ] |
1534 | + |
1535 | + def open_products(self, cr, uid, ids, context=None): |
1536 | + txt = [] |
1537 | + for d in self.browse(cr, uid, ids, context=context): |
1538 | + txt.append('%s %s not %s' % (d.nomenclature.name, d.field, d.value)) |
1539 | + |
1540 | + return { |
1541 | + 'type': 'ir.actions.act_window', |
1542 | + 'res_model': 'product.product', |
1543 | + 'view_type': 'form', |
1544 | + 'view_mode': 'tree,form', |
1545 | + 'domain': [('incompatible_oc_default_values', 'in', ids)], |
1546 | + 'name': ' or '.join(txt) |
1547 | + } |
1548 | + |
1549 | +unidata_default_product_value() |
1550 | + |
1551 | +class unidata_pull_product_log(osv.osv): |
1552 | + _name = 'unidata.pull_product.log' |
1553 | + _order = 'id desc' |
1554 | + def _get_json_data_formated(self, cr, uid, ids, field_name, args, context=None): |
1555 | + res = {} |
1556 | + |
1557 | + for j in self.browse(cr, uid, ids, fields_to_fetch=['json_data'], context=context): |
1558 | + try: |
1559 | + res[j.id] = pprint.pformat(safe_eval(j.json_data), width=160) |
1560 | + except: |
1561 | + res[j.id] = False |
1562 | + return res |
1563 | + |
1564 | + _columns = { |
1565 | + 'msfid': fields.integer('MSF ID', required=1), |
1566 | + 'code': fields.char('UD Code', size=64, select=1), |
1567 | + 'former_codes': fields.char('Former Code', size=1024, select=1), |
1568 | + 'date': fields.datetime('Date', required=1, select=1), |
1569 | + 'log': fields.text('Log'), |
1570 | + 'json_data': fields.text('UD Json'), |
1571 | + 'json_data_formated': fields.function(_get_json_data_formated, method=1, type='text',string='UD Json'), |
1572 | + 'session_id': fields.many2one('unidata.sync.log', 'Session', select=1), |
1573 | + } |
1574 | + |
1575 | + _defaults = { |
1576 | + 'date': lambda *a, **b: fields.datetime.now(), |
1577 | + } |
1578 | +unidata_pull_product_log() |
1579 | + |
1580 | +class unidata_products_error(osv.osv): |
1581 | + _name = 'unidata.products_error' |
1582 | + _order = 'date desc, msfid' |
1583 | + def _get_json_data_formated(self, cr, uid, ids, field_name, args, context=None): |
1584 | + res = {} |
1585 | + |
1586 | + for j in self.browse(cr, uid, ids, fields_to_fetch=['json_data'], context=context): |
1587 | + try: |
1588 | + res[j.id] = pprint.pformat(safe_eval(j.json_data), width=160) |
1589 | + except: |
1590 | + res[j.id] = False |
1591 | + return res |
1592 | + |
1593 | + _columns = { |
1594 | + 'msfid': fields.integer('MSF ID', required=1, select=1), |
1595 | + 'code': fields.char('UD Code', size=64, select=1), |
1596 | + 'former_codes': fields.char('Former Code', size=1024), |
1597 | + 'date': fields.datetime('Date of last error', required=1, select=1), |
1598 | + 'fixed_date': fields.datetime('Fixed at', select=1), |
1599 | + 'log': fields.text('Log'), |
1600 | + 'uf_product_id': fields.text('UF product db id'), |
1601 | + 'json_data': fields.text('UD Json'), |
1602 | + 'json_data_formated': fields.function(_get_json_data_formated, method=1, type='text',string='UD Json'), |
1603 | + } |
1604 | + |
1605 | + _sql_constraints = [ |
1606 | + ('unique_msfid', 'unique(msfid)', 'msfid already exists.'), |
1607 | + ] |
1608 | + _defaults = { |
1609 | + 'date': lambda *a, **b: fields.datetime.now(), |
1610 | + } |
1611 | +unidata_products_error() |
1612 | |
1613 | === modified file 'bin/addons/product_attributes/unidata_sync.xml' |
1614 | --- bin/addons/product_attributes/unidata_sync.xml 2023-11-28 13:36:32 +0000 |
1615 | +++ bin/addons/product_attributes/unidata_sync.xml 2024-05-13 13:47:13 +0000 |
1616 | @@ -120,11 +120,29 @@ |
1617 | <newline /> |
1618 | <field name="number_products_pulled" /> |
1619 | <field name="number_products_updated" /> |
1620 | + <field name="number_products_created" /> |
1621 | + <field name="number_products_errors" /> |
1622 | <field name="msfid_min" /> |
1623 | <field name="last_date" /> |
1624 | <field name="sync_type" /> |
1625 | <field name="state" /> |
1626 | <field name="error" colspan="4" /> |
1627 | + <field name="sync_error" colspan="4" nolabel="1"> |
1628 | + <tree string="Product Errors"> |
1629 | + <field name="msfid" /> |
1630 | + <field name="code" /> |
1631 | + <field name="former_codes" /> |
1632 | + <field name="log" /> |
1633 | + <field name="date" /> |
1634 | + </tree> |
1635 | + <form string="Product Errors"> |
1636 | + <field name="msfid" /> |
1637 | + <field name="code" /> |
1638 | + <field name="former_codes" /> |
1639 | + <field name="log" /> |
1640 | + <field name="json_data_formated" colspan="4" widget="full_text" nolabel="1"/> |
1641 | + </form> |
1642 | + </field> |
1643 | </form> |
1644 | </field> |
1645 | </record> |
1646 | @@ -139,7 +157,9 @@ |
1647 | <field name="end_date" /> |
1648 | <field name="page_size" /> |
1649 | <field name="number_products_pulled" /> |
1650 | - <field name="number_products_updated" /> |
1651 | + <field name="number_products_updated" /> |
1652 | + <field name="number_products_created" /> |
1653 | + <field name="number_products_errors" /> |
1654 | <field name="sync_type" /> |
1655 | <field name="state" /> |
1656 | <field name="log_exists" invisible="1" /> |
1657 | @@ -708,5 +728,143 @@ |
1658 | key2="client_action_multi" |
1659 | search_view_id="product_msl_rel_search" |
1660 | context="{'search_default_product_id': active_id}"/> |
1661 | + |
1662 | + <record id="unidata_sync_log_msl_action" model="ir.actions.act_window"> |
1663 | + <field name="name">MSL Sync report</field> |
1664 | + <field name="res_model">unidata.sync.log</field> |
1665 | + <field name="view_type">form</field> |
1666 | + <field name="view_mode">tree,form</field> |
1667 | + <field name="search_view_id" ref="unidata_sync_log_msl_search" /> |
1668 | + <field name="view_id" ref="unidata_sync_log_msl_tree" /> |
1669 | + <field name="domain">[('server', '=', 'msl')]</field> |
1670 | + </record> |
1671 | + |
1672 | + <record id="unidata_products_error_action" model="ir.actions.act_window"> |
1673 | + <field name="name">UniData linkage errors</field> |
1674 | + <field name="res_model">unidata.products_error</field> |
1675 | + <field name="view_type">form</field> |
1676 | + <field name="view_mode">tree,form</field> |
1677 | + <field name="domain">[]</field> |
1678 | + <field name="context">{'search_default_hidde_fixed': True}</field> |
1679 | + </record> |
1680 | + |
1681 | + <record model="ir.ui.view" id="unidata_products_error_form"> |
1682 | + <field name="name">unidata.products_error.form</field> |
1683 | + <field name="model">unidata.products_error</field> |
1684 | + <field name="type">form</field> |
1685 | + <field name="arch" type="xml"> |
1686 | + <form string="UniData linkage error" hide_new_button="1" hide_delete_button="1" hide_duplicate_button="1"> |
1687 | + <field name="date" /> |
1688 | + <field name="fixed_date" /> |
1689 | + <field name="msfid" /> |
1690 | + <field name="uf_product_id" /> |
1691 | + <field name="code" /> |
1692 | + <field name="former_codes" /> |
1693 | + <field name="log" colspan="4" nolabel="1" /> |
1694 | + <field name="json_data_formated" widget="full_text" nolabel="1" colspan="4" /> |
1695 | + </form> |
1696 | + </field> |
1697 | + </record> |
1698 | + |
1699 | + <record model="ir.ui.view" id="unidata_products_error_tree"> |
1700 | + <field name="name">unidata.products_error.tree</field> |
1701 | + <field name="model">unidata.products_error</field> |
1702 | + <field name="type">tree</field> |
1703 | + <field name="priority" eval="20" /> |
1704 | + <field name="arch" type="xml"> |
1705 | + <tree string="UniData linkage errors" hide_new_button="1" hide_delete_button="1" noteditable="1" colors="red:not fixed_date"> |
1706 | + <field name="date" /> |
1707 | + <field name="fixed_date" /> |
1708 | + <field name="msfid" /> |
1709 | + <field name="uf_product_id" /> |
1710 | + <field name="code" /> |
1711 | + <field name="former_codes" /> |
1712 | + <field name="log" /> |
1713 | + </tree> |
1714 | + </field> |
1715 | + </record> |
1716 | + |
1717 | + <record model="ir.ui.view" id="unidata_products_error_search"> |
1718 | + <field name="name">unidata.products_error.search</field> |
1719 | + <field name="model">unidata.products_error</field> |
1720 | + <field name="type">search</field> |
1721 | + <field name="arch" type="xml"> |
1722 | + <search string="UniData linkage errors"> |
1723 | + <filter name="hidde_fixed" string="Hide Fixed" domain="[('fixed_date', '=', False)]" icon="terp-check" /> |
1724 | + <field name="date" /> |
1725 | + <field name="fixed_date" /> |
1726 | + <field name="msfid" /> |
1727 | + <newline /> |
1728 | + <field name="uf_product_id" /> |
1729 | + <field name="code" /> |
1730 | + <field name="former_codes" /> |
1731 | + <field name="log" /> |
1732 | + </search> |
1733 | + </field> |
1734 | + </record> |
1735 | + <menuitem name="UniData linkage errors" action="unidata_products_error_action" id="unidata_products_error_menu" parent="unidata_main_menu" sequence="26"/> |
1736 | + |
1737 | + <record id="unidata_products_not_oc_value_action" model="ir.actions.act_window"> |
1738 | + <field name="name">UD Products incompatible with OC values</field> |
1739 | + <field name="res_model">product.product</field> |
1740 | + <field name="view_type">form</field> |
1741 | + <field name="view_mode">tree,form</field> |
1742 | + <field name="domain">[('incompatible_oc_default_values', '=', True)]</field> |
1743 | + <field name="context">{}</field> |
1744 | + </record> |
1745 | + |
1746 | + <menuitem name="UD Products incompatible with OC values" action="unidata_products_not_oc_value_action" id="unidata_products_not_oc_value_menu" parent="unidata_main_menu" sequence="27"/> |
1747 | + |
1748 | + <record id="unidata_default_product_value_action" model="ir.actions.act_window"> |
1749 | + <field name="name">OC Default values</field> |
1750 | + <field name="res_model">unidata.default_product_value</field> |
1751 | + <field name="view_type">form</field> |
1752 | + <field name="view_mode">tree,form</field> |
1753 | + <field name="domain">[]</field> |
1754 | + <field name="context">{}</field> |
1755 | + </record> |
1756 | + |
1757 | + <record model="ir.ui.view" id="unidata_default_product_value_form"> |
1758 | + <field name="name">unidata.default_product_value.form</field> |
1759 | + <field name="model">unidata.default_product_value</field> |
1760 | + <field name="type">form</field> |
1761 | + <field name="arch" type="xml"> |
1762 | + <form string="OC Default Values" hide_duplicate_button="1"> |
1763 | + <field name="nomenclature" /> |
1764 | + <field name="field" /> |
1765 | + <field name="value" /> |
1766 | + </form> |
1767 | + </field> |
1768 | + </record> |
1769 | + |
1770 | + <record model="ir.ui.view" id="unidata_default_product_value_tree"> |
1771 | + <field name="name">unidata.default_product_value.tree</field> |
1772 | + <field name="model">unidata.default_product_value</field> |
1773 | + <field name="type">tree</field> |
1774 | + <field name="priority" eval="20" /> |
1775 | + <field name="arch" type="xml"> |
1776 | + <tree string="OC Default Values"> |
1777 | + <field name="nomenclature" /> |
1778 | + <field name="field" /> |
1779 | + <field name="value" /> |
1780 | + <field name="number_incompatible_products" /> |
1781 | + <button name="open_products" string="View products" icon="gtk-go-forward" type="object" attrs="{'invisible': [('number_incompatible_products', '=', 0)]}" /> |
1782 | + </tree> |
1783 | + </field> |
1784 | + </record> |
1785 | + |
1786 | + <record model="ir.ui.view" id="unidata_default_product_value_search"> |
1787 | + <field name="name">unidata.default_product_value.search</field> |
1788 | + <field name="model">unidata.default_product_value</field> |
1789 | + <field name="type">search</field> |
1790 | + <field name="arch" type="xml"> |
1791 | + <search string="OC Default Values"> |
1792 | + <field name="nomenclature" /> |
1793 | + <field name="field" /> |
1794 | + <field name="value" /> |
1795 | + </search> |
1796 | + </field> |
1797 | + </record> |
1798 | + <menuitem name="OC Default Values" action="unidata_default_product_value_action" id="unidata_default_product_value_menu" parent="unidata_main_menu" sequence="27"/> |
1799 | </data> |
1800 | </openerp> |
1801 | |
1802 | === added directory 'tools/UD' |
1803 | === added file 'tools/UD/create_sql.py' |
1804 | --- tools/UD/create_sql.py 1970-01-01 00:00:00 +0000 |
1805 | +++ tools/UD/create_sql.py 2024-05-13 13:47:13 +0000 |
1806 | @@ -0,0 +1,99 @@ |
1807 | +#!/usr/bin/env python |
1808 | + |
1809 | +import psycopg2 |
1810 | +import sys |
1811 | +import csv |
1812 | + |
1813 | +if len(sys.argv) < 3: |
1814 | + print('%s dbname oc' % sys.argv[0]) |
1815 | + sys.exit(1) |
1816 | + |
1817 | +dbname = sys.argv[1] |
1818 | +oc = sys.argv[2] |
1819 | + |
1820 | +if oc not in ('OCP', 'OCA', 'OCG', 'OCB'): |
1821 | + print('OC unknown') |
1822 | + sys.exit(1) |
1823 | + |
1824 | +db_host = '127.0.0.1' |
1825 | + |
1826 | +conn = psycopg2.connect(database=dbname) |
1827 | +cr = conn.cursor() |
1828 | + |
1829 | +line_number = 1 |
1830 | + |
1831 | +values_mapping = { |
1832 | + 'Yes': 't', |
1833 | + 'Kit/Module': 'kit', |
1834 | + 'Make to Order': 'make_to_order', |
1835 | + 'Service with Reception': 'service_recep', |
1836 | + '': '', |
1837 | +} |
1838 | +with open('ud_default_value.csv', 'r', newline='') as f: |
1839 | + c = csv.reader(f, quotechar='"', delimiter=',') |
1840 | + for line in c: |
1841 | + line_number += 1 |
1842 | + if not line or line[1] != oc: |
1843 | + continue |
1844 | + nomen = line[3][5:].strip() |
1845 | + all_nom = nomen.split('|') |
1846 | + level = 1 |
1847 | + parent_id = False |
1848 | + error = False |
1849 | + |
1850 | + if line[4] not in values_mapping: |
1851 | + print('Line number %s, value %s not found' % (line_number, line[4])) |
1852 | + break |
1853 | + for nom in all_nom: |
1854 | + nom = nom.strip()[0:63] |
1855 | + if nom: |
1856 | + cond = '' |
1857 | + params = [nom.strip(), level] |
1858 | + if parent_id: |
1859 | + cond = ' and n.parent_id in %s' |
1860 | + params.append(tuple(parent_id)) |
1861 | + params[0] = '%%%s' % nom |
1862 | + |
1863 | + cr.execute(""" |
1864 | + select |
1865 | + n.id |
1866 | + from |
1867 | + product_nomenclature n |
1868 | + left join ir_translation t on t.lang='en_MF' and t.name='product.nomenclature,name' and t.res_id = n.id |
1869 | + where |
1870 | + coalesce(t.value, n.name) like %s and |
1871 | + n.level=%s |
1872 | + """+cond, tuple(params)) # not_a_user_entry |
1873 | + if not cr.rowcount: |
1874 | + print(all_nom) |
1875 | + print(str(cr.mogrify(""" |
1876 | + select |
1877 | + n.id |
1878 | + from |
1879 | + product_nomenclature n |
1880 | + left join ir_translation t on t.lang='en_MF' and t.name='product.nomenclature,name' and t.res_id = n.id |
1881 | + where |
1882 | + coalesce(t.value, n.name) like %s and |
1883 | + n.level=%s |
1884 | + """+cond, tuple(params)), 'utf8')) |
1885 | + print('Line number %s, nomen %s not found' % (line_number, nom)) |
1886 | + error = True |
1887 | + break |
1888 | + parent_id = [x[0] for x in cr.fetchall()] |
1889 | + level += 1 |
1890 | + if error: |
1891 | + continue |
1892 | + for n_id in parent_id: |
1893 | + query = "insert into unidata_default_product_value (field, value, nomenclature) values (%s, %s, %s);" |
1894 | + values = (line[2], values_mapping[line[4]], n_id) |
1895 | + print('-- %s' %line) |
1896 | + print(str(cr.mogrify(query, values), 'utf8')) |
1897 | + cr.execute(query, values) |
1898 | + |
1899 | +query = "update product_cold_chain set ud_code=code;" |
1900 | +print(query) |
1901 | +cr.execute(query) |
1902 | +query = "update product_cold_chain set ud_code='CT3+' where id in (select res_id from ir_model_data where name='product_attributes_cold_20');" |
1903 | +print(query) |
1904 | +cr.execute(query) |
1905 | +conn.commit() |