Merge lp:~julie-w/unifield-server/US-7903 into lp:unifield-server
- US-7903
- Merge into trunk
Proposed by
jftempo
Status: | Merged |
---|---|
Merged at revision: | 6055 |
Proposed branch: | lp:~julie-w/unifield-server/US-7903 |
Merge into: | lp:unifield-server |
Diff against target: |
1870 lines (+697/-403) (has conflicts) 19 files modified
bin/addons/account_override/account_invoice_sync.py (+44/-33) bin/addons/account_override/invoice.py (+14/-2) bin/addons/analytic_distribution/account_commitment.py (+100/-25) bin/addons/analytic_distribution/account_commitment_view.xml (+45/-9) bin/addons/analytic_distribution/account_commitment_workflow.xml (+16/-0) bin/addons/analytic_distribution_supply/invoice.py (+150/-107) bin/addons/analytic_distribution_supply/stock.py (+16/-32) bin/addons/base/res/res_log.py (+2/-0) bin/addons/msf_profile/data/patches.xml (+7/-0) bin/addons/msf_profile/i18n/fr_MF.po (+94/-15) bin/addons/msf_profile/msf_profile.py (+13/-0) bin/addons/purchase/purchase_order.py (+10/-1) bin/addons/purchase/purchase_order_line.py (+53/-25) bin/addons/purchase/stock.py (+0/-52) bin/addons/sale/stock.py (+0/-59) bin/addons/stock/stock.py (+116/-19) bin/addons/stock_override/stock.py (+0/-23) bin/addons/sync_so/purchase.py (+7/-1) bin/osv/expression.py (+10/-0) Text conflict in bin/addons/msf_profile/data/patches.xml Text conflict in bin/addons/msf_profile/i18n/fr_MF.po Text conflict in bin/addons/msf_profile/msf_profile.py Text conflict in bin/osv/expression.py |
To merge this branch: | bzr merge lp:~julie-w/unifield-server/US-7903 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
UniField Reviewer Team | Pending | ||
Review via email: mp+406907@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/addons/account_override/account_invoice_sync.py' |
2 | --- bin/addons/account_override/account_invoice_sync.py 2021-01-26 11:14:14 +0000 |
3 | +++ bin/addons/account_override/account_invoice_sync.py 2021-08-11 08:19:49 +0000 |
4 | @@ -100,10 +100,43 @@ |
5 | line_name = inv_line.get('name', '') |
6 | if not line_name: # required field |
7 | raise osv.except_osv(_('Error'), _("Impossible to retrieve the line description.")) |
8 | + uom_id = False |
9 | + uom_data = inv_line.get('uos_id', {}) |
10 | + if uom_data: |
11 | + uom_name = uom_data.get('name', '') |
12 | + uom_ids = product_uom_obj.search(cr, uid, [('name', '=', uom_name)], limit=1, context=context) |
13 | + if not uom_ids: |
14 | + raise osv.except_osv(_('Error'), _("Unit of Measure %s not found.") % uom_name) |
15 | + uom_id = uom_ids[0] |
16 | + quantity = inv_line.get('quantity', 0.0) |
17 | + inv_line_vals = { |
18 | + 'invoice_id': inv_id, |
19 | + 'name': line_name, |
20 | + 'quantity': quantity, |
21 | + 'price_unit': inv_line.get('price_unit', 0.0), |
22 | + 'discount': inv_line.get('discount', 0.0), |
23 | + 'uos_id': uom_id, |
24 | + } |
25 | + line_account_id = False |
26 | + fo_line_dict = inv_line.get('sale_order_line_id') or {} |
27 | + if from_supply and inv_linked_po and fo_line_dict.get('sync_local_id'): |
28 | + # fill in the AD at line level if applicable |
29 | + # search the matching between PO line and invoice line |
30 | + po_line_ids = pol_obj.search(cr, uid, [('order_id', '=', inv_linked_po.id), ('sync_linked_sol', '=', inv_line['sale_order_line_id']['sync_local_id'])], context=context) |
31 | + if po_line_ids: |
32 | + matching_po_line = pol_obj.browse(cr, uid, po_line_ids[0], |
33 | + fields_to_fetch=['analytic_distribution_id', 'cv_line_ids'], context=context) |
34 | + inv_line_vals.update({'order_line_id': matching_po_line.id}) |
35 | + if matching_po_line.cv_line_ids: |
36 | + inv_line_vals.update({'cv_line_ids': [(6, 0, [cvl.id for cvl in matching_po_line.cv_line_ids])]}) |
37 | + # cv_line_ids only contains one CV line: get its account |
38 | + line_account_id = matching_po_line.cv_line_ids[0].account_id.id |
39 | + po_line_distrib = matching_po_line.analytic_distribution_id |
40 | + self._create_analytic_distrib(cr, uid, inv_line_vals, po_line_distrib, context=context) # update inv_line_vals |
41 | product_id = False |
42 | product_data = inv_line.get('product_id', {}) |
43 | - line_account_id = False |
44 | - # for the lines related to a product: use the account of the product / else use the one of the source invoice line |
45 | + # for the lines linked to a CV: the CV line account is used (handled above) |
46 | + # for the other lines related to a product: use the account of the product / else use the one of the source invoice line |
47 | if product_data: |
48 | default_code = product_data.get('default_code', '') |
49 | product_id = so_po_common_obj.get_product_id(cr, uid, product_data, default_code=default_code, context=context) or False |
50 | @@ -113,10 +146,11 @@ |
51 | context=context) |
52 | if not product.active: |
53 | raise osv.except_osv(_('Error'), _("The product %s is inactive.") % product.default_code or '') |
54 | - line_account_id = product.product_tmpl_id.property_account_expense and product.product_tmpl_id.property_account_expense.id |
55 | + if not line_account_id: |
56 | + line_account_id = product.product_tmpl_id.property_account_expense and product.product_tmpl_id.property_account_expense.id |
57 | if not line_account_id: |
58 | line_account_id = product.categ_id and product.categ_id.property_account_expense_categ and product.categ_id.property_account_expense_categ.id |
59 | - else: |
60 | + elif not line_account_id: |
61 | account_code = inv_line.get('account_id', {}).get('code', '') |
62 | if not account_code: |
63 | raise osv.except_osv(_('Error'), _("Impossible to retrieve the account code at line level.")) |
64 | @@ -131,34 +165,9 @@ |
65 | if inv_posting_date < line_account.activation_date or \ |
66 | (line_account.inactivation_date and inv_posting_date >= line_account.inactivation_date): |
67 | raise osv.except_osv(_('Error'), _('The account "%s - %s" is inactive.') % (line_account.code, line_account.name)) |
68 | - uom_id = False |
69 | - uom_data = inv_line.get('uos_id', {}) |
70 | - if uom_data: |
71 | - uom_name = uom_data.get('name', '') |
72 | - uom_ids = product_uom_obj.search(cr, uid, [('name', '=', uom_name)], limit=1, context=context) |
73 | - if not uom_ids: |
74 | - raise osv.except_osv(_('Error'), _("Unit of Measure %s not found.") % uom_name) |
75 | - uom_id = uom_ids[0] |
76 | - quantity = inv_line.get('quantity', 0.0) |
77 | - inv_line_vals = { |
78 | - 'invoice_id': inv_id, |
79 | - 'account_id': line_account_id, |
80 | - 'name': line_name, |
81 | - 'quantity': quantity, |
82 | - 'price_unit': inv_line.get('price_unit', 0.0), |
83 | - 'discount': inv_line.get('discount', 0.0), |
84 | - 'product_id': product_id, |
85 | - 'uos_id': uom_id, |
86 | - } |
87 | - fo_line_dict = inv_line.get('sale_order_line_id') or {} |
88 | - if from_supply and inv_linked_po and fo_line_dict.get('sync_local_id'): |
89 | - # fill in the AD at line level if applicable |
90 | - # search the matching between PO line and invoice line |
91 | - po_line_ids = pol_obj.search(cr, uid, [('order_id', '=', inv_linked_po.id), ('sync_linked_sol', '=', inv_line['sale_order_line_id']['sync_local_id'])], context=context) |
92 | - if po_line_ids: |
93 | - matching_po_line = pol_obj.browse(cr, uid, po_line_ids[0], fields_to_fetch=['analytic_distribution_id'], context=context) |
94 | - po_line_distrib = matching_po_line.analytic_distribution_id |
95 | - self._create_analytic_distrib(cr, uid, inv_line_vals, po_line_distrib, context=context) # update inv_line_vals |
96 | + inv_line_vals.update({'account_id': line_account_id, |
97 | + 'product_id': product_id, |
98 | + }) |
99 | inv_line_obj.create(cr, uid, inv_line_vals, context=context) |
100 | |
101 | def create_invoice_from_sync(self, cr, uid, source, invoice_data, context=None): |
102 | @@ -270,7 +279,9 @@ |
103 | if po_ids: |
104 | po_id = po_ids[0] |
105 | if po_id: |
106 | - vals.update({'main_purchase_id': po_id}) |
107 | + vals.update({'main_purchase_id': po_id, |
108 | + 'purchase_ids': [(6, 0, [po_id])], |
109 | + }) |
110 | po_fields = ['picking_ids', 'analytic_distribution_id', 'order_line', 'name'] |
111 | po = po_obj.browse(cr, uid, po_id, fields_to_fetch=po_fields, context=context) |
112 | po_number = po.name |
113 | |
114 | === modified file 'bin/addons/account_override/invoice.py' |
115 | --- bin/addons/account_override/invoice.py 2021-04-29 16:00:54 +0000 |
116 | +++ bin/addons/account_override/invoice.py 2021-08-11 08:19:49 +0000 |
117 | @@ -1423,6 +1423,8 @@ |
118 | vals = by_account_vals[l.account_id.id] |
119 | if l.order_line_id: |
120 | vals.setdefault('purchase_order_line_ids', []).append(l.order_line_id.id) |
121 | + if l.cv_line_ids: |
122 | + vals.setdefault('cv_line_ids', []).extend([cvl.id for cvl in l.cv_line_ids]) |
123 | else: |
124 | # new account to merge |
125 | vals = vals_template.copy() |
126 | @@ -1430,9 +1432,12 @@ |
127 | '_index_': index, |
128 | 'account_id': l.account_id.id, |
129 | 'purchase_order_line_ids': [], |
130 | + 'cv_line_ids': [], |
131 | }) |
132 | if l.order_line_id: |
133 | vals['purchase_order_line_ids'].append(l.order_line_id.id) |
134 | + if l.cv_line_ids: |
135 | + vals['cv_line_ids'].extend([cvl.id for cvl in l.cv_line_ids]) |
136 | index += 1 |
137 | |
138 | ''' |
139 | @@ -1515,6 +1520,8 @@ |
140 | |
141 | vals['purchase_order_line_ids'] = vals['purchase_order_line_ids'] and [(6, 0, vals['purchase_order_line_ids'])] or False |
142 | |
143 | + vals['cv_line_ids'] = vals['cv_line_ids'] and [(6, 0, vals['cv_line_ids'])] or False |
144 | + |
145 | # create merge line |
146 | vals.update({'merged_line': True}) |
147 | if not self.pool.get('account.invoice.line').create(cr, uid, |
148 | @@ -1797,6 +1804,10 @@ |
149 | ('out_refund', 'Customer Refund'), |
150 | ('in_refund', 'Supplier Refund')]), |
151 | 'merged_line': fields.boolean(string='Merged Line', help='Line generated by the merging of other lines', readonly=True), |
152 | + # - a CV line can be linked to several invoice lines ==> e.g. several partial deliveries, split of invoice lines |
153 | + # - an invoice line can be linked to several CV lines => e.g. merge invoice lines by account |
154 | + 'cv_line_ids': fields.many2many('account.commitment.line', 'inv_line_cv_line_rel', 'inv_line_id', 'cv_line_id', |
155 | + string='Commitment Voucher Lines'), |
156 | } |
157 | |
158 | _defaults = { |
159 | @@ -1943,7 +1954,7 @@ |
160 | """ |
161 | Copy an invoice line without its move lines, |
162 | without the link to a reversed invoice line, |
163 | - and without link to PO/FO lines when the duplication is manual |
164 | + and without link to PO/FO/CV lines when the duplication is manual |
165 | Reset the merged_line tag. |
166 | """ |
167 | if context is None: |
168 | @@ -1955,13 +1966,14 @@ |
169 | 'merged_line': False, |
170 | }) |
171 | # Manual duplication should generate a "manual document not created through the supply workflow" |
172 | - # so we don't keep the link to PO/FO at line level |
173 | + # so we don't keep the link to PO/FO/CV at line level |
174 | if context.get('from_button') and not context.get('from_split'): |
175 | default.update({ |
176 | 'order_line_id': False, |
177 | 'sale_order_line_id': False, |
178 | 'sale_order_lines': False, |
179 | 'purchase_order_line_ids': [], |
180 | + 'cv_line_ids': [(6, 0, [])], |
181 | }) |
182 | return super(account_invoice_line, self).copy_data(cr, uid, inv_id, default, context) |
183 | |
184 | |
185 | === modified file 'bin/addons/analytic_distribution/account_commitment.py' |
186 | --- bin/addons/analytic_distribution/account_commitment.py 2021-05-11 18:16:37 +0000 |
187 | +++ bin/addons/analytic_distribution/account_commitment.py 2021-08-11 08:19:49 +0000 |
188 | @@ -67,6 +67,38 @@ |
189 | res.append(cvl.commit_id.id) |
190 | return res |
191 | |
192 | + def get_cv_type(self, cr, uid, context=None): |
193 | + """ |
194 | + Returns the list of possible types for the Commitment Vouchers |
195 | + """ |
196 | + return [('manual', 'Manual'), |
197 | + ('external', 'Automatic - External supplier'), |
198 | + ('esc', 'Manual - ESC supplier'), |
199 | + ('intermission', 'Automatic - Intermission'), |
200 | + ('intersection', 'Automatic - Intersection'), |
201 | + ] |
202 | + |
203 | + def get_current_cv_version(self, cr, uid, context=None): |
204 | + """ |
205 | + Version 2 since US-7449 |
206 | + """ |
207 | + return 2 |
208 | + |
209 | + def _display_super_done_button(self, cr, uid, ids, name, arg, context=None): |
210 | + """ |
211 | + For now the "Super" Done button, which allows to always set a CV to Done whatever its state and origin, |
212 | + is visible only by the Admin user. It is displayed only when the standard Done button isn't usable. |
213 | + """ |
214 | + if context is None: |
215 | + context = {} |
216 | + if isinstance(ids, (int, long)): |
217 | + ids = [ids] |
218 | + res = {} |
219 | + for cv in self.read(cr, uid, ids, ['state', 'type'], context=context): |
220 | + other_done_button_usable = cv['state'] == 'open' and cv['type'] not in ('external', 'intermission', 'intersection') |
221 | + res[cv['id']] = not other_done_button_usable and uid == 1 and cv['state'] != 'done' |
222 | + return res |
223 | + |
224 | _columns = { |
225 | 'journal_id': fields.many2one('account.analytic.journal', string="Journal", readonly=True, required=True), |
226 | 'name': fields.char(string="Number", size=64, readonly=True, required=True), |
227 | @@ -81,16 +113,22 @@ |
228 | 'account.commitment.line': (_get_cv, ['amount'],10), |
229 | }), |
230 | 'analytic_distribution_id': fields.many2one('analytic.distribution', string="Analytic distribution"), |
231 | - 'type': fields.selection([('manual', 'Manual'), ('external', 'Automatic - External supplier'), ('esc', 'Manual - ESC supplier')], string="Type", readonly=True), |
232 | + 'type': fields.selection(get_cv_type, string="Type", readonly=True), |
233 | 'notes': fields.text(string="Comment"), |
234 | 'purchase_id': fields.many2one('purchase.order', string="Source document", readonly=True), |
235 | 'description': fields.char(string="Description", size=256), |
236 | + 'version': fields.integer('Version', required=True, |
237 | + help="Technical field to distinguish old CVs from new ones which have a different behavior."), |
238 | + 'display_super_done_button': fields.function(_display_super_done_button, method=True, type='boolean', |
239 | + store=False, invisible=True, |
240 | + string='Display the button allowing to always set a CV to Done'), |
241 | } |
242 | |
243 | _defaults = { |
244 | 'state': lambda *a: 'draft', |
245 | 'date': lambda *a: strftime('%Y-%m-%d'), |
246 | 'type': lambda *a: 'manual', |
247 | + 'version': get_current_cv_version, |
248 | 'journal_id': lambda s, cr, uid, c: s.pool.get('account.analytic.journal').search(cr, uid, [('type', '=', 'engagement'), |
249 | ('instance_id', '=', s.pool.get('res.users').browse(cr, uid, uid, c).company_id.instance_id.id)], limit=1, context=c)[0] |
250 | } |
251 | @@ -181,29 +219,30 @@ |
252 | fctal_currency = user_obj.browse(cr, uid, uid, fields_to_fetch=['company_id'], context=context).company_id.currency_id.id |
253 | for cl in c.line_ids: |
254 | # Verify that date is compatible with all analytic account from distribution |
255 | + distrib = False |
256 | if cl.analytic_distribution_id: |
257 | distrib = cl.analytic_distribution_id |
258 | elif cl.commit_id and cl.commit_id.analytic_distribution_id: |
259 | distrib = cl.commit_id.analytic_distribution_id |
260 | - else: |
261 | - raise osv.except_osv(_('Warning'), _('No analytic distribution found for %s %s') % (cl.account_id.code, cl.initial_amount)) |
262 | - for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]: |
263 | - for distrib_line in distrib_lines: |
264 | - if distrib_line.analytic_id and \ |
265 | - (distrib_line.analytic_id.date_start and date < distrib_line.analytic_id.date_start or |
266 | - distrib_line.analytic_id.date and date >= distrib_line.analytic_id.date): |
267 | - raise osv.except_osv(_('Error'), _('The analytic account %s is not active for given date.') % |
268 | - (distrib_line.analytic_id.name,)) |
269 | - dest_cc_tuples = set() # check each Dest/CC combination only once |
270 | - for distrib_cc_l in distrib.cost_center_lines: |
271 | - if distrib_cc_l.analytic_id: # non mandatory field |
272 | - dest_cc_tuples.add((distrib_cc_l.destination_id, distrib_cc_l.analytic_id)) |
273 | - for distrib_fp_l in distrib.funding_pool_lines: |
274 | - dest_cc_tuples.add((distrib_fp_l.destination_id, distrib_fp_l.cost_center_id)) |
275 | - for dest, cc in dest_cc_tuples: |
276 | - if dest_cc_link_obj.is_inactive_dcl(cr, uid, dest.id, cc.id, date, context=context): |
277 | - raise osv.except_osv(_('Error'), _("The combination \"%s - %s\" is not active at this date: %s") % |
278 | - (dest.code or '', cc.code or '', date)) |
279 | + if distrib: |
280 | + for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, |
281 | + distrib.free_1_lines, distrib.free_2_lines]: |
282 | + for distrib_line in distrib_lines: |
283 | + if distrib_line.analytic_id and \ |
284 | + (distrib_line.analytic_id.date_start and date < distrib_line.analytic_id.date_start or |
285 | + distrib_line.analytic_id.date and date >= distrib_line.analytic_id.date): |
286 | + raise osv.except_osv(_('Error'), _('The analytic account %s is not active for given date.') % |
287 | + (distrib_line.analytic_id.name,)) |
288 | + dest_cc_tuples = set() # check each Dest/CC combination only once |
289 | + for distrib_cc_l in distrib.cost_center_lines: |
290 | + if distrib_cc_l.analytic_id: # non mandatory field |
291 | + dest_cc_tuples.add((distrib_cc_l.destination_id, distrib_cc_l.analytic_id)) |
292 | + for distrib_fp_l in distrib.funding_pool_lines: |
293 | + dest_cc_tuples.add((distrib_fp_l.destination_id, distrib_fp_l.cost_center_id)) |
294 | + for dest, cc in dest_cc_tuples: |
295 | + if dest_cc_link_obj.is_inactive_dcl(cr, uid, dest.id, cc.id, date, context=context): |
296 | + raise osv.except_osv(_('Error'), _("The combination \"%s - %s\" is not active at this date: %s") % |
297 | + (dest.code or '', cc.code or '', date)) |
298 | # update the dates and fctal amounts of the related analytic lines |
299 | context.update({'currency_date': date}) # same date used for doc, posting and source date of all lines |
300 | for aal in cl.analytic_lines: |
301 | @@ -233,6 +272,7 @@ |
302 | default.update({ |
303 | 'name': self.pool.get('ir.sequence').get(cr, uid, 'account.commitment'), |
304 | 'state': 'draft', |
305 | + 'version': self.get_current_cv_version(cr, uid, context=context), |
306 | }) |
307 | # Default method |
308 | res = super(account_commitment, self).copy(cr, uid, c_id, default, context) |
309 | @@ -312,6 +352,12 @@ |
310 | 'context': context, |
311 | } |
312 | |
313 | + def button_analytic_distribution_2(self, cr, uid, ids, context=None): |
314 | + """ |
315 | + This is just an alias for button_analytic_distribution (used to have different names and attrs on both buttons) |
316 | + """ |
317 | + return self.button_analytic_distribution(cr, uid, ids, context=context) |
318 | + |
319 | def button_reset_distribution(self, cr, uid, ids, context=None): |
320 | """ |
321 | Reset analytic distribution on all commitment lines. |
322 | @@ -444,10 +490,11 @@ |
323 | # Search analytic lines that have commitment line ids |
324 | search_ids = self.pool.get('account.analytic.line').search(cr, uid, [('commitment_line_id', 'in', [x.id for x in c.line_ids])], context=context) |
325 | # Delete them |
326 | - res = self.pool.get('account.analytic.line').unlink(cr, uid, search_ids, context=context) |
327 | + if search_ids: |
328 | + res = self.pool.get('account.analytic.line').unlink(cr, uid, search_ids, context=context) |
329 | + if not res: |
330 | + raise osv.except_osv(_('Error'), _('An error occurred on engagement lines deletion.')) |
331 | # And finally update commitment voucher state and lines amount |
332 | - if not res: |
333 | - raise osv.except_osv(_('Error'), _('An error occurred on engagement lines deletion.')) |
334 | self.pool.get('account.commitment.line').write(cr, uid, [x.id for x in c.line_ids], {'amount': 0}, context=context) |
335 | self.write(cr, uid, [c.id], {'state':'done'}, context=context) |
336 | return True |
337 | @@ -457,7 +504,7 @@ |
338 | class account_commitment_line(osv.osv): |
339 | _name = 'account.commitment.line' |
340 | _description = "Account Commitment Voucher Line" |
341 | - _order = "id desc" |
342 | + _order = "po_line_id, id desc" |
343 | _rec_name = 'account_id' |
344 | _trace = True |
345 | |
346 | @@ -497,6 +544,12 @@ |
347 | res[co.id] = False |
348 | return res |
349 | |
350 | + def get_cv_type(self, cr, uid, context=None): |
351 | + """ |
352 | + Gets the possible CV types |
353 | + """ |
354 | + return self.pool.get('account.commitment').get_cv_type(cr, uid, context) |
355 | + |
356 | _columns = { |
357 | 'account_id': fields.many2one('account.account', string="Account", required=True), |
358 | 'amount': fields.float(string="Amount left", digits_compute=dp.get_precision('Account'), required=False), |
359 | @@ -504,6 +557,8 @@ |
360 | 'commit_id': fields.many2one('account.commitment', string="Commitment Voucher", on_delete="cascade"), |
361 | 'commit_number': fields.related('commit_id', 'name', type='char', size=64, |
362 | readonly=True, store=False, string="Commitment Voucher Number"), |
363 | + 'commit_type': fields.related('commit_id', 'type', string="Commitment Voucher Type", type='selection', readonly=True, |
364 | + store=False, invisible=True, selection=get_cv_type, write_relate=False), |
365 | 'analytic_distribution_id': fields.many2one('analytic.distribution', string="Analytic distribution"), |
366 | 'analytic_distribution_state': fields.function(_get_distribution_state, method=True, type='selection', |
367 | selection=[('none', 'None'), ('valid', 'Valid'), ('invalid', 'Invalid')], |
368 | @@ -513,8 +568,15 @@ |
369 | 'analytic_lines': fields.one2many('account.analytic.line', 'commitment_line_id', string="Analytic Lines"), |
370 | 'first': fields.boolean(string="Is not created?", help="Useful for onchange method for views. Should be False after line creation.", |
371 | readonly=True), |
372 | + # for CV in version 1 |
373 | 'purchase_order_line_ids': fields.many2many('purchase.order.line', 'purchase_line_commitment_rel', 'commitment_id', 'purchase_id', |
374 | - string="Purchase Order Lines", readonly=True), |
375 | + string="Purchase Order Lines (deprecated)", readonly=True), |
376 | + # for CV starting from version 2 |
377 | + 'po_line_id': fields.many2one('purchase.order.line', "PO Line"), |
378 | + 'po_line_product_id': fields.related('po_line_id', 'product_id', type='many2one', relation='product.product', |
379 | + string="Product", readonly=True, store=True, write_relate=False), |
380 | + 'po_line_number': fields.related('po_line_id', 'line_number', type='integer_null', string="PO Line", readonly=True, |
381 | + store=True, write_relate=False, _fnct_migrate=lambda *a: True), |
382 | } |
383 | |
384 | _defaults = { |
385 | @@ -656,6 +718,19 @@ |
386 | self.update_analytic_lines(cr, uid, [line.id], vals.get('amount'), account_id, context=context) |
387 | return super(account_commitment_line, self).write(cr, uid, ids, vals, context={}) |
388 | |
389 | + def copy_data(self, cr, uid, cv_line_id, default=None, context=None): |
390 | + """ |
391 | + Duplicates a CV line: resets the link to PO line |
392 | + """ |
393 | + if context is None: |
394 | + context = {} |
395 | + if default is None: |
396 | + default = {} |
397 | + default.update({ |
398 | + 'po_line_id': False, |
399 | + }) |
400 | + return super(account_commitment_line, self).copy_data(cr, uid, cv_line_id, default, context) |
401 | + |
402 | def button_analytic_distribution(self, cr, uid, ids, context=None): |
403 | """ |
404 | Launch analytic distribution wizard on a commitment voucher line |
405 | |
406 | === modified file 'bin/addons/analytic_distribution/account_commitment_view.xml' |
407 | --- bin/addons/analytic_distribution/account_commitment_view.xml 2020-09-23 09:29:11 +0000 |
408 | +++ bin/addons/analytic_distribution/account_commitment_view.xml 2021-08-11 08:19:49 +0000 |
409 | @@ -28,20 +28,36 @@ |
410 | <button name="button_reset_distribution" string="Reset AD at line level" type="object" icon="gtk-undelete" colspan="4" states="draft"/> |
411 | </group> |
412 | <group colspan="8" col="8" attrs="{'invisible': [('analytic_distribution_id', '!=', False)]}"> |
413 | - <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-emblem-important" context="context" colspan="4" attrs="{'invisible': [('analytic_distribution_id', '!=', False)]}"/> |
414 | + <button name="button_analytic_distribution_2" string="Analytical Distribution" |
415 | + type="object" icon="terp-emblem-important" context="context" colspan="4" |
416 | + attrs="{'readonly': [('state', '=', 'open'), ('type', '=', 'manual')]}"/> |
417 | <button name="button_reset_distribution" string="Reset AD at line level" type="object" icon="gtk-undelete" colspan="4" states="draft"/> |
418 | </group> |
419 | <field name="analytic_distribution_id" invisible="1"/> |
420 | <notebook colspan="4"> |
421 | <page string="Commitment voucher lines"> |
422 | - <field name="line_ids" nolabel="1" colspan="4" attrs="{'readonly': ['|', ('state', '=', 'done'), ('type', '=', 'external')]}"/> |
423 | + <field name="line_ids" nolabel="1" colspan="4" |
424 | + attrs="{'readonly': ['|', ('state', '=', 'done'), |
425 | + '&', |
426 | + ('type', 'in', ['external', 'intermission', 'intersection']), |
427 | + ('state', '!=', 'draft')]}" |
428 | + /> |
429 | </page> |
430 | </notebook> |
431 | <field name="notes" colspan="4"/> |
432 | <group col="6" colspan="4"> |
433 | <button name="button_compute" string="Compute total" icon="gtk-execute" colspan="2"/> |
434 | <button name="commitment_open" string="Validate" icon="terp-camera_test" states="draft" colspan="2"/> |
435 | - <button name="commitment_validate" string="Done" icon="terp-gtk-go-back-rtl" states="open" colspan="2" attrs="{'readonly': [('type', '=', 'external')]}"/> |
436 | + <field name="display_super_done_button"/> |
437 | + <button name="commitment_validate" string="Done" icon="terp-gtk-go-back-rtl" colspan="2" |
438 | + attrs="{'invisible': ['|', ('state', '!=', 'open'), ('display_super_done_button', '=', True)], |
439 | + 'readonly': [('type', 'in', ['external', 'intermission', 'intersection'])]}" |
440 | + /> |
441 | + <!-- button which allows to always set a CV to Done (whatever its state and origin) --> |
442 | + <button name="commitment_always_validate" string="Done (for Administrator only)" |
443 | + icon="terp-gtk-go-back-rtl" colspan="6" |
444 | + confirm='You are about to set this Commitment Voucher to the state "Done". Do you want to proceed?' |
445 | + attrs="{'invisible': [('display_super_done_button', '=', False)]}"/> |
446 | </group> |
447 | <field name="state"/> |
448 | <field name="total"/> |
449 | @@ -73,14 +89,32 @@ |
450 | <field name="model">account.commitment.line</field> |
451 | <field name="type">tree</field> |
452 | <field name="arch" type="xml"> |
453 | - <tree string="Commitment Voucher Lines" editable="top" colors="red:analytic_distribution_state == 'invalid'"> |
454 | + <!-- display the "New" button or not depending on the state and type of the CV itself --> |
455 | + <tree string="Commitment Voucher Lines" editable="top" colors="red:analytic_distribution_state == 'invalid'" |
456 | + button_attrs="{'invisible': ['|', ('state', '=', 'done'), ('type', 'in', ['external', 'intermission', 'intersection'])]}" |
457 | + hide_delete_button="1" |
458 | + > |
459 | + <field name="commit_type"/> |
460 | + <field name="po_line_product_id"/> |
461 | + <field name="po_line_number"/> |
462 | <field name="account_id" domain="[('restricted_area', '=', 'commitment_lines')]"/> |
463 | - <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-stock_symbol-selection" context="context"/> |
464 | - <field name="analytic_distribution_state"/> |
465 | - <field name="have_analytic_distribution_from_header"/> |
466 | + <button name="button_analytic_distribution" string="Analytical Distribution" type="object" |
467 | + icon="terp-stock_symbol-selection" context="context" |
468 | + /> |
469 | + <field name="analytic_distribution_state" readonly="1"/> |
470 | + <field name="have_analytic_distribution_from_header" readonly="1"/> |
471 | <field name="first" invisible="1"/> |
472 | - <field name="initial_amount" on_change="onchange_initial_amount(first, initial_amount)"/> |
473 | - <field name="amount" attrs="{'readonly': [('first', '=', True)]}"/> |
474 | + <field name="initial_amount" on_change="onchange_initial_amount(first, initial_amount)" |
475 | + attrs="{'readonly': [('commit_type', 'in', ['external', 'intermission', 'intersection'])]}" |
476 | + /> |
477 | + <field name="amount" |
478 | + attrs="{'readonly': ['|', ('first', '=', True), ('commit_type', 'in', ['external', 'intermission', 'intersection'])]}" |
479 | + /> |
480 | + <!-- display the "Delete" button or not depending on the state and type of the CV itself --> |
481 | + <button name="unlink" string="Delete" icon="gtk-del" type="object" |
482 | + attrs="{'invisible': ['|', ('state', '=', 'done'), ('type', 'in', ['external', 'intermission', 'intersection'])]}" |
483 | + confirm="Do you really want to delete this line?" |
484 | + /> |
485 | </tree> |
486 | </field> |
487 | </record> |
488 | @@ -95,6 +129,8 @@ |
489 | <group col='6' colspan='4'> |
490 | <filter icon="terp-tools" string="Manual" domain="[('type','=','manual')]" help="Manual Commitment Voucher"/> |
491 | <filter icon="gtk-quit" string="External" domain="[('type','=','external')]" help="External Commitment Voucher"/> |
492 | + <filter icon="gtk-refresh" string="Intersection" domain="[('type', '=', 'intersection')]" help="Intersection Commitment Voucher"/> |
493 | + <filter icon="gtk-ok" string="Intermission" domain="[('type', '=', 'intermission')]" help="Intermission Commitment Voucher"/> |
494 | <filter icon="terp-partner" string="ESC" domain="[('type','=','esc')]" help="ESC Commitment Voucher"/> |
495 | <separator orientation="vertical"/> |
496 | <filter icon="terp-document-new" string="Draft" domain="[('state','=','draft')]" help="Commitment Voucher in Draft state" name="draft"/> |
497 | |
498 | === modified file 'bin/addons/analytic_distribution/account_commitment_workflow.xml' |
499 | --- bin/addons/analytic_distribution/account_commitment_workflow.xml 2011-11-30 14:56:38 +0000 |
500 | +++ bin/addons/analytic_distribution/account_commitment_workflow.xml 2021-08-11 08:19:49 +0000 |
501 | @@ -34,14 +34,30 @@ |
502 | <record id="commit_t1" model="workflow.transition"> |
503 | <field name="act_from" ref="act_draft"/> |
504 | <field name="act_to" ref="act_open"/> |
505 | + <field name="sequence">10</field> |
506 | <field name="signal">commitment_open</field> |
507 | </record> |
508 | |
509 | <record id="commit_t2" model="workflow.transition"> |
510 | <field name="act_from" ref="act_open"/> |
511 | <field name="act_to" ref="act_done"/> |
512 | + <field name="sequence">10</field> |
513 | <field name="signal">commitment_validate</field> |
514 | </record> |
515 | |
516 | + <record id="commit_t3" model="workflow.transition"> |
517 | + <field name="act_from" ref="act_open"/> |
518 | + <field name="act_to" ref="act_done"/> |
519 | + <field name="sequence">15</field> <!-- (sequence, act_from) must be unique --> |
520 | + <field name="signal">commitment_always_validate</field> |
521 | + </record> |
522 | + |
523 | + <record id="commit_t4" model="workflow.transition"> |
524 | + <field name="act_from" ref="act_draft"/> |
525 | + <field name="act_to" ref="act_done"/> |
526 | + <field name="sequence">15</field> <!-- (sequence, act_from) must be unique --> |
527 | + <field name="signal">commitment_always_validate</field> |
528 | + </record> |
529 | + |
530 | </data> |
531 | </openerp> |
532 | |
533 | === modified file 'bin/addons/analytic_distribution_supply/invoice.py' |
534 | --- bin/addons/analytic_distribution_supply/invoice.py 2021-01-26 13:48:04 +0000 |
535 | +++ bin/addons/analytic_distribution_supply/invoice.py 2021-08-11 08:19:49 +0000 |
536 | @@ -26,6 +26,7 @@ |
537 | from osv import fields |
538 | from tools.translate import _ |
539 | from base import currency_date |
540 | +import netsvc |
541 | |
542 | |
543 | class account_invoice_line(osv.osv): |
544 | @@ -93,20 +94,27 @@ |
545 | self.pool.get('account.invoice').write(cr, uid, [inv.id], {'analytic_distribution_id': new_distrib_id,}) |
546 | # Then set distribution on invoice line regarding purchase order line distribution |
547 | for invl in inv.invoice_line: |
548 | - if invl.order_line_id: |
549 | + line_distrib_id = False |
550 | + if invl.cv_line_ids: |
551 | + # CV STARTING FROM VERSION 2 |
552 | + # the first CV line found is used since there can be only one at this step (merging lines by account could |
553 | + # generate an invoice line linked to several CV lines but this action can only be done later in the process) |
554 | + line_distrib_id = invl.cv_line_ids[0].analytic_distribution_id and invl.cv_line_ids[0].analytic_distribution_id.id or False |
555 | + elif invl.order_line_id: |
556 | # Fetch PO line analytic distribution or nothing (that implies it take those from PO) |
557 | - distrib_id = invl.order_line_id.analytic_distribution_id and invl.order_line_id.analytic_distribution_id.id or False |
558 | + line_distrib_id = invl.order_line_id.analytic_distribution_id and invl.order_line_id.analytic_distribution_id.id or False |
559 | # Attempt to fetch commitment line analytic distribution or commitment voucher analytic distribution or default distrib_id |
560 | + # CV IN VERSION 1 |
561 | if invl.order_line_id.commitment_line_ids: |
562 | - distrib_id = invl.order_line_id.commitment_line_ids[0].analytic_distribution_id \ |
563 | - and invl.order_line_id.commitment_line_ids[0].analytic_distribution_id.id or distrib_id |
564 | - if distrib_id: |
565 | - new_invl_distrib_id = ana_obj.copy(cr, uid, distrib_id, {}) |
566 | - if not new_invl_distrib_id: |
567 | - raise osv.except_osv(_('Error'), _('An error occurred for analytic distribution copy for invoice.')) |
568 | - # create default funding pool lines |
569 | - ana_obj.create_funding_pool_lines(cr, uid, [new_invl_distrib_id], invl.account_id.id) |
570 | - invl_obj.write(cr, uid, [invl.id], {'analytic_distribution_id': new_invl_distrib_id}) |
571 | + line_distrib_id = invl.order_line_id.commitment_line_ids[0].analytic_distribution_id \ |
572 | + and invl.order_line_id.commitment_line_ids[0].analytic_distribution_id.id or line_distrib_id |
573 | + if line_distrib_id: |
574 | + new_invl_distrib_id = ana_obj.copy(cr, uid, line_distrib_id, {}) |
575 | + if not new_invl_distrib_id: |
576 | + raise osv.except_osv(_('Error'), _('An error occurred for analytic distribution copy for invoice.')) |
577 | + # create default funding pool lines |
578 | + ana_obj.create_funding_pool_lines(cr, uid, [new_invl_distrib_id], invl.account_id.id) |
579 | + invl_obj.write(cr, uid, [invl.id], {'analytic_distribution_id': new_invl_distrib_id}) |
580 | # Fetch SO line analytic distribution |
581 | # sol AD copy moved into _invoice_line_hook |
582 | return True |
583 | @@ -124,7 +132,8 @@ |
584 | |
585 | # Browse invoices |
586 | for inv in self.browse(cr, uid, ids, context=context): |
587 | - grouped_invl = {} |
588 | + grouped_invl_by_acc = {} |
589 | + grouped_invl_by_cvl = {} |
590 | co_ids = self.pool.get('account.commitment').search(cr, uid, [('purchase_id', 'in', [x.id for x in inv.purchase_ids]), ('state', 'in', ['open', 'draft'])], order='date desc', context=context) |
591 | if not co_ids: |
592 | continue |
593 | @@ -133,27 +142,50 @@ |
594 | # Do not take invoice line that have no order_line_id (so that are not linked to a purchase order line) |
595 | if not invl.order_line_id and not inv.is_merged_by_account: |
596 | continue |
597 | - |
598 | - # Fetch purchase order line account |
599 | - if inv.is_merged_by_account: |
600 | - if not invl.account_id: |
601 | - continue |
602 | - # US-357: lines without product (get directly account) |
603 | - a = invl.account_id.id |
604 | - else: |
605 | - pol = invl.order_line_id |
606 | - a = self._get_expense_account(cr, uid, pol, context=context) |
607 | - if pol.product_id and not a: |
608 | - raise osv.except_osv(_('Error !'), _('There is no expense account defined for this product: "%s" (id:%d)') % (pol.product_id.name, pol.product_id.id)) |
609 | - elif not a: |
610 | - raise osv.except_osv(_('Error !'), _('There is no expense account defined for this PO line: "%s" (id:%d)') % (pol.line_number, pol.id)) |
611 | - if a not in grouped_invl: |
612 | - grouped_invl[a] = 0 |
613 | - |
614 | - grouped_invl[a] += invl.price_subtotal |
615 | + # exclude push flow |
616 | + if invl.order_line_id and (invl.order_line_id.order_id.push_fo or invl.order_line_id.set_as_sourced_n): |
617 | + continue |
618 | + old_cv_version = True |
619 | + # CV STARTING FROM VERSION 2 |
620 | + amount_to_subtract = invl.price_subtotal or 0.0 |
621 | + for cv_line in invl.cv_line_ids: |
622 | + old_cv_version = False # the field cv_line_ids exist for CVs starting from version 2 |
623 | + if abs(amount_to_subtract) <= 10**-3: |
624 | + break |
625 | + cvl_amount_left = cv_line.amount or 0.0 |
626 | + if cvl_amount_left: |
627 | + if cv_line.id not in grouped_invl_by_cvl: |
628 | + grouped_invl_by_cvl[cv_line.id] = 0 |
629 | + if amount_to_subtract >= cvl_amount_left: |
630 | + grouped_invl_by_cvl[cv_line.id] += cvl_amount_left |
631 | + amount_to_subtract -= cvl_amount_left |
632 | + else: |
633 | + grouped_invl_by_cvl[cv_line.id] += amount_to_subtract |
634 | + amount_to_subtract = 0 |
635 | + # CV IN VERSION 1 |
636 | + if old_cv_version: |
637 | + # Fetch purchase order line account |
638 | + if inv.is_merged_by_account: |
639 | + if not invl.account_id: |
640 | + continue |
641 | + # US-357: lines without product (get directly account) |
642 | + a = invl.account_id.id |
643 | + else: |
644 | + pol = invl.order_line_id |
645 | + a = self._get_expense_account(cr, uid, pol, context=context) |
646 | + if pol.product_id and not a: |
647 | + raise osv.except_osv(_('Error !'), _('There is no expense account defined for this product: "%s" (id:%d)') % |
648 | + (pol.product_id.name, pol.product_id.id)) |
649 | + elif not a: |
650 | + raise osv.except_osv(_('Error !'), _('There is no expense account defined for this PO line: "%s" (id:%d)') % |
651 | + (pol.line_number, pol.id)) |
652 | + if a not in grouped_invl_by_acc: |
653 | + grouped_invl_by_acc[a] = 0 |
654 | + grouped_invl_by_acc[a] += invl.price_subtotal |
655 | |
656 | po_ids = [x.id for x in inv.purchase_ids] |
657 | - self._update_commitments_lines(cr, uid, po_ids, grouped_invl, from_cancel=False, context=context) |
658 | + self._update_commitments_lines(cr, uid, po_ids, account_amount_dic=grouped_invl_by_acc, |
659 | + cvl_amount_dic=grouped_invl_by_cvl, from_cancel=False, context=context) |
660 | |
661 | return True |
662 | |
663 | @@ -170,38 +202,51 @@ |
664 | |
665 | return account_id |
666 | |
667 | - def _update_commitments_lines(self, cr, uid, po_ids, account_amount_dic, from_cancel=False, context=None): |
668 | + def _update_commitments_lines(self, cr, uid, po_ids, account_amount_dic=None, cvl_amount_dic=None, from_cancel=False, context=None): |
669 | """ |
670 | po_ids: list of PO ids |
671 | account_amount_dic: dict, keys are G/L account_id, values are amount to deduce |
672 | |
673 | |
674 | """ |
675 | - if not po_ids or not account_amount_dic: |
676 | + if not po_ids or not account_amount_dic and not cvl_amount_dic: |
677 | return True |
678 | |
679 | if context is None: |
680 | context = {} |
681 | + if account_amount_dic is None: |
682 | + account_amount_dic = {} |
683 | + if cvl_amount_dic is None: |
684 | + cvl_amount_dic = {} |
685 | + wf_service = netsvc.LocalService("workflow") |
686 | |
687 | # po is state=cancel on last IN cancel |
688 | company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id |
689 | - cr.execute('''select l.id, l.account_id, l.commit_id, c.state, l.amount, l.analytic_distribution_id, c.analytic_distribution_id, c.id, c.currency_id, c.type from |
690 | + # avoids empty lists so that the SQL request can be executed |
691 | + account_list = account_amount_dic.keys() or [0] |
692 | + cvl_list = cvl_amount_dic.keys() or [0] |
693 | + cr.execute('''select l.id, l.account_id, l.commit_id, c.state, l.amount, l.analytic_distribution_id, c.analytic_distribution_id, |
694 | + c.id, c.currency_id, c.type, c.version from |
695 | account_commitment_line l, account_commitment c |
696 | where l.commit_id = c.id and |
697 | l.amount > 0 and |
698 | c.purchase_id in %s and |
699 | - l.account_id in %s and |
700 | + (c.version < 2 and l.account_id in %s or c.version >= 2 and l.id in %s) and |
701 | c.state in ('open', 'draft') |
702 | order by c.date asc |
703 | - ''', (tuple(po_ids), tuple(account_amount_dic.keys())) |
704 | + ''', (tuple(po_ids), tuple(account_list), tuple(cvl_list)) |
705 | ) |
706 | # sort all cv lines by account / cv date |
707 | - cv_info = {} |
708 | + account_info = {} |
709 | + cvl_info = {} |
710 | auto_cv = True |
711 | for cv in cr.fetchall(): |
712 | - if cv[1] not in cv_info: |
713 | - cv_info[cv[1]] = [] |
714 | - cv_info[cv[1]].append(cv) |
715 | + if cv[10] < 2: |
716 | + # CV IN VERSION 1 |
717 | + account_info.setdefault(cv[1], []).append(cv) # key = account id |
718 | + else: |
719 | + # CV STARTING FROM VERSION 2 |
720 | + cvl_info.setdefault(cv[0], []).append(cv) # key = CV line id |
721 | if cv[9] == 'manual': |
722 | auto_cv = False |
723 | |
724 | @@ -210,76 +255,74 @@ |
725 | cv_to_close = {} |
726 | |
727 | # deduce amount on oldest cv lines |
728 | - for account in account_amount_dic.keys(): |
729 | - if account not in cv_info: |
730 | - continue |
731 | - for cv_line in cv_info[account]: |
732 | - if cv_line[3] == 'draft' and cv_line[2] not in draft_opened and not from_cancel: |
733 | - draft_opened.append(cv_line[2]) |
734 | - # If Commitment voucher in draft state we change it to 'validated' without using workflow and engagement lines generation |
735 | - # NB: This permits to avoid modification on commitment voucher when receiving some goods |
736 | - self.pool.get('account.commitment').write(cr, uid, [cv_line[2]], {'state': 'open'}, context=context) |
737 | - |
738 | - if cv_line[4] - account_amount_dic[account] > 0.001: |
739 | - # update amount left on CV line |
740 | - amount_left = cv_line[4] - account_amount_dic[account] |
741 | - self.pool.get('account.commitment.line').write(cr, uid, [cv_line[0]], {'amount': amount_left}, context=context) |
742 | - |
743 | - # update AAL |
744 | - distrib_id = cv_line[5] or cv_line[6] |
745 | - if not distrib_id: |
746 | - raise osv.except_osv(_('Error'), _('No analytic distribution found.')) |
747 | - |
748 | - # Browse distribution |
749 | - distrib = self.pool.get('analytic.distribution').browse(cr, uid, [distrib_id], context=context)[0] |
750 | - engagement_lines = distrib.analytic_lines |
751 | - for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]: |
752 | - for distrib_line in distrib_lines: |
753 | - vals = { |
754 | - 'account_id': distrib_line.analytic_id.id, |
755 | - 'general_account_id': account, |
756 | - } |
757 | - if distrib_line._name == 'funding.pool.distribution.line': |
758 | - vals.update({'cost_center_id': distrib_line.cost_center_id and distrib_line.cost_center_id.id or False,}) |
759 | + # NOTE: account_amount_dic is for CV in version 1 based on accounts (acc), |
760 | + # cvl_amount_dic is for CV from version 2 based on CV lines (cvl) |
761 | + for c_type in ("acc", "cvl"): |
762 | + if c_type == "acc": |
763 | + cv_info = account_info.copy() |
764 | + amount_dic = account_amount_dic.copy() |
765 | + else: |
766 | + cv_info = cvl_info.copy() |
767 | + amount_dic = cvl_amount_dic.copy() |
768 | + for k in amount_dic.keys(): # account id or CV line id |
769 | + if k not in cv_info: |
770 | + continue |
771 | + for cv_line in cv_info[k]: |
772 | + if cv_line[3] == 'draft' and cv_line[2] not in draft_opened and not from_cancel: |
773 | + draft_opened.append(cv_line[2]) |
774 | + # Change Draft CV to Validated State, in order to avoid CV modification when receiving some goods. |
775 | + # The workflow is used so that all the engagement lines are generated, even those which are not |
776 | + # affected by the current update (e.g. partial reception + SI validation on one in 2 products). |
777 | + wf_service.trg_validate(uid, 'account.commitment', cv_line[2], 'commitment_open', cr) |
778 | + |
779 | + if cv_line[4] - amount_dic[k] > 0.001: |
780 | + # update amount left on CV line |
781 | + amount_left = cv_line[4] - amount_dic[k] |
782 | + self.pool.get('account.commitment.line').write(cr, uid, [cv_line[0]], {'amount': amount_left}, context=context) |
783 | + # update AAL |
784 | + distrib_id = cv_line[5] or cv_line[6] |
785 | + if not distrib_id: |
786 | + raise osv.except_osv(_('Error'), _('No analytic distribution found.')) |
787 | + # Browse distribution |
788 | + distrib = self.pool.get('analytic.distribution').browse(cr, uid, [distrib_id], context=context)[0] |
789 | + engagement_lines = distrib.analytic_lines |
790 | + for distrib_line in distrib.funding_pool_lines: |
791 | # Browse engagement lines to found out matching elements |
792 | - for i in range(0,len(engagement_lines)): |
793 | + for i in range(0, len(engagement_lines)): |
794 | if engagement_lines[i]: |
795 | eng_line = engagement_lines[i] |
796 | - cmp_vals = { |
797 | - 'account_id': eng_line.account_id.id, |
798 | - 'general_account_id': eng_line.general_account_id.id, |
799 | - } |
800 | - if eng_line.cost_center_id: |
801 | - cmp_vals.update({'cost_center_id': eng_line.cost_center_id.id}) |
802 | - if cmp_vals == vals: |
803 | - # Update analytic line with new amount |
804 | - anal_amount = (distrib_line.percentage * amount_left) / 100 |
805 | - curr_date = currency_date.get_date(self, cr, eng_line.document_date, eng_line.date, |
806 | - source_date=eng_line.source_date) |
807 | - context.update({'currency_date': curr_date}) |
808 | - amount = -1 * self.pool.get('res.currency').compute(cr, uid, cv_line[8], company_currency, |
809 | - anal_amount, round=False, context=context) |
810 | - |
811 | - # write new amount to corresponding engagement line |
812 | - self.pool.get('account.analytic.line').write(cr, uid, [eng_line.id], |
813 | - {'amount': amount, 'amount_currency': -1 * anal_amount}, context=context) |
814 | - |
815 | - # check next G/L account |
816 | - break |
817 | - |
818 | - cv_to_close[cv_line[2]] = True |
819 | - eng_ids = self.pool.get('account.analytic.line').search(cr, uid, [('commitment_line_id', '=', cv_line[0])], context=context) |
820 | - if eng_ids: |
821 | - self.pool.get('account.analytic.line').unlink(cr, uid, eng_ids, context=context) |
822 | - self.pool.get('account.commitment.line').write(cr, uid, [cv_line[0]], {'amount': 0.0}, context=context) |
823 | - if abs(cv_line[4] - account_amount_dic[account]) < 0.001: |
824 | - # check next G/L account |
825 | - break |
826 | - |
827 | - # check next CV on this account |
828 | - account_amount_dic[account] -= cv_line[4] |
829 | - |
830 | - if auto_cv and from_cancel: |
831 | + # restrict to the current CV line only |
832 | + if eng_line.commitment_line_id and eng_line.commitment_line_id.id == cv_line[0]: |
833 | + eng_line_distrib_id = eng_line.distrib_line_id and \ |
834 | + eng_line.distrib_line_id._name == 'funding.pool.distribution.line' and \ |
835 | + eng_line.distrib_line_id.id or False |
836 | + # in case of an AD with several lines, several AJIs are linked to the same CV line: |
837 | + # the comparison is used to decrement the right one |
838 | + if eng_line_distrib_id == distrib_line.id: |
839 | + # Update analytic line with new amount |
840 | + anal_amount = (distrib_line.percentage * amount_left) / 100 |
841 | + curr_date = currency_date.get_date(self, cr, eng_line.document_date, eng_line.date, |
842 | + source_date=eng_line.source_date) |
843 | + context.update({'currency_date': curr_date}) |
844 | + amount = -1 * self.pool.get('res.currency').compute(cr, uid, cv_line[8], company_currency, |
845 | + anal_amount, round=False, context=context) |
846 | + # write new amount to corresponding engagement line |
847 | + self.pool.get('account.analytic.line').write(cr, uid, [eng_line.id], |
848 | + {'amount': amount, |
849 | + 'amount_currency': -1 * anal_amount}, context=context) |
850 | + # check next G/L account or CV line |
851 | + break |
852 | + cv_to_close[cv_line[2]] = True |
853 | + eng_ids = self.pool.get('account.analytic.line').search(cr, uid, [('commitment_line_id', '=', cv_line[0])], context=context) |
854 | + if eng_ids: |
855 | + self.pool.get('account.analytic.line').unlink(cr, uid, eng_ids, context=context) |
856 | + self.pool.get('account.commitment.line').write(cr, uid, [cv_line[0]], {'amount': 0.0}, context=context) |
857 | + if abs(cv_line[4] - amount_dic[k]) < 0.001: |
858 | + # check next G/L account or CV line |
859 | + break |
860 | + amount_dic[k] -= cv_line[4] |
861 | + |
862 | + if auto_cv and from_cancel and from_cancel is not True: |
863 | # we cancel the last IN from PO and no draft invoice exist |
864 | if not self.pool.get('account.invoice').search_exist(cr, uid, [('purchase_ids', 'in', po_ids), ('state', '=', 'draft')], context=context): |
865 | dpo_ids = self.pool.get('purchase.order').search(cr, uid, [('id', 'in', po_ids), ('po_version', '!=', 1), ('order_type', '=', 'direct')], context=context) |
866 | |
867 | === modified file 'bin/addons/analytic_distribution_supply/stock.py' |
868 | --- bin/addons/analytic_distribution_supply/stock.py 2018-09-04 17:49:19 +0000 |
869 | +++ bin/addons/analytic_distribution_supply/stock.py 2021-08-11 08:19:49 +0000 |
870 | @@ -23,30 +23,6 @@ |
871 | |
872 | from osv import osv |
873 | |
874 | -class stock_picking(osv.osv): |
875 | - _name = 'stock.picking' |
876 | - _inherit = 'stock.picking' |
877 | - |
878 | - |
879 | - def _invoice_hook(self, cr, uid, picking, invoice_id): |
880 | - """ |
881 | - Create a link between invoice and purchase_order. |
882 | - Copy analytic distribution from purchase order to invoice (or from commitment voucher if exists) |
883 | - """ |
884 | - if invoice_id and picking: |
885 | - po_id = picking.purchase_id and picking.purchase_id.id or False |
886 | - so_id = picking.sale_id and picking.sale_id.id or False |
887 | - if po_id: |
888 | - self.pool.get('purchase.order').write(cr, uid, [po_id], {'invoice_ids': [(4, invoice_id)]}) |
889 | - if so_id: |
890 | - self.pool.get('sale.order').write(cr, uid, [so_id], {'invoice_ids': [(4, invoice_id)]}) |
891 | - # Copy analytic distribution from purchase order or commitment voucher (if exists) or sale order |
892 | - self.pool.get('account.invoice').fetch_analytic_distribution(cr, uid, [invoice_id]) |
893 | - return super(stock_picking, self)._invoice_hook(cr, uid, picking, invoice_id) |
894 | - |
895 | -# action_invoice_create method have been removed because of impossibility to retrieve DESTINATION from SO. |
896 | - |
897 | -stock_picking() |
898 | |
899 | class stock_move(osv.osv): |
900 | _name = 'stock.move' |
901 | @@ -64,6 +40,7 @@ |
902 | |
903 | inv_obj = self.pool.get('account.invoice') |
904 | account_amount = {} |
905 | + cvl_amount = {} |
906 | po_ids = {} |
907 | for move in self.browse(cr, uid, ids, context=context): |
908 | # Fetch all necessary elements |
909 | @@ -80,14 +57,21 @@ |
910 | continue |
911 | |
912 | po_ids[move.purchase_line_id.order_id.id] = True |
913 | - account_id = inv_obj._get_expense_account(cr, uid, move.purchase_line_id, context=context) |
914 | - if account_id: |
915 | - if account_id not in account_amount: |
916 | - account_amount[account_id] = 0 |
917 | - account_amount[account_id] += round(qty * price_unit, 2) |
918 | - |
919 | - if account_amount and po_ids: |
920 | - inv_obj._update_commitments_lines(cr, uid, po_ids.keys(), account_amount, from_cancel=ids, context=context) |
921 | + cv_line = move.purchase_line_id.cv_line_ids and move.purchase_line_id.cv_line_ids[0] or False |
922 | + cv_version = cv_line and cv_line.commit_id and cv_line.commit_id.version or 1 |
923 | + if cv_version > 1: |
924 | + if cv_line.id not in cvl_amount: |
925 | + cvl_amount[cv_line.id] = 0 |
926 | + cvl_amount[cv_line.id] += round(qty * price_unit, 2) |
927 | + else: |
928 | + account_id = inv_obj._get_expense_account(cr, uid, move.purchase_line_id, context=context) |
929 | + if account_id: |
930 | + if account_id not in account_amount: |
931 | + account_amount[account_id] = 0 |
932 | + account_amount[account_id] += round(qty * price_unit, 2) |
933 | + if (account_amount or cvl_amount) and po_ids: |
934 | + inv_obj._update_commitments_lines(cr, uid, po_ids.keys(), account_amount_dic=account_amount, cvl_amount_dic=cvl_amount, |
935 | + from_cancel=ids, context=context) |
936 | |
937 | return super(stock_move, self).action_cancel(cr, uid, ids, context=context) |
938 | |
939 | |
940 | === modified file 'bin/addons/base/res/res_log.py' |
941 | --- bin/addons/base/res/res_log.py 2018-08-06 13:07:33 +0000 |
942 | +++ bin/addons/base/res/res_log.py 2021-08-11 08:19:49 +0000 |
943 | @@ -55,6 +55,8 @@ |
944 | create_context = context and dict(context) or {} |
945 | if 'res_log_read' in create_context: |
946 | vals['read'] = create_context.pop('res_log_read') |
947 | + if '__copy_data_seen' in create_context: |
948 | + create_context.pop('__copy_data_seen') |
949 | if create_context and not vals.get('context'): |
950 | vals['context'] = create_context |
951 | return super(res_log, self).create(cr, uid, vals, context=context) |
952 | |
953 | === modified file 'bin/addons/delivery_mechanism/delivery_mechanism.py' |
954 | === modified file 'bin/addons/msf_profile/data/patches.xml' |
955 | --- bin/addons/msf_profile/data/patches.xml 2021-08-05 13:10:04 +0000 |
956 | +++ bin/addons/msf_profile/data/patches.xml 2021-08-11 08:19:49 +0000 |
957 | @@ -677,6 +677,7 @@ |
958 | <field name="method">us_8753_admin_never_expire_password</field> |
959 | </record> |
960 | |
961 | +<<<<<<< TREE |
962 | <!-- UF22.0 --> |
963 | <record id="us_8805_product_set_archived" model="patch.scripts"> |
964 | <field name="method">us_8805_product_set_archived</field> |
965 | @@ -686,5 +687,11 @@ |
966 | <field name="method">us_8869_remove_ir_import</field> |
967 | </record> |
968 | |
969 | +======= |
970 | + <!-- UF22.0 --> |
971 | + <record id="us_7449_set_cv_version" model="patch.scripts"> |
972 | + <field name="method">us_7449_set_cv_version</field> |
973 | + </record> |
974 | +>>>>>>> MERGE-SOURCE |
975 | </data> |
976 | </openerp> |
977 | |
978 | === modified file 'bin/addons/msf_profile/i18n/fr_MF.po' |
979 | --- bin/addons/msf_profile/i18n/fr_MF.po 2021-08-11 05:45:06 +0000 |
980 | +++ bin/addons/msf_profile/i18n/fr_MF.po 2021-08-11 08:19:49 +0000 |
981 | @@ -3925,6 +3925,7 @@ |
982 | #: field:account.invoice.tax,manual:0 |
983 | #: view:account.commitment:0 |
984 | #: selection:account.commitment,type:0 |
985 | +#: selection:account.commitment.line,commit_type:0 |
986 | #: selection:purchase.order,invoice_method:0 |
987 | #: model:ir.ui.menu,name:sync_client.sync_wiz_menu |
988 | msgid "Manual" |
989 | @@ -9634,6 +9635,7 @@ |
990 | |
991 | #. module: analytic_distribution |
992 | #: selection:account.commitment,type:0 |
993 | +#: selection:account.commitment.line,commit_type:0 |
994 | msgid "Manual - ESC supplier" |
995 | msgstr "Manuel - Fournisseur ESC" |
996 | |
997 | @@ -30297,7 +30299,7 @@ |
998 | #: code:addons/purchase/purchase_order_line.py:1795 |
999 | #, python-format |
1000 | msgid "There is no expense account defined for this product: \"%s\" (id:%d)" |
1001 | -msgstr "Il n'y a pas de compte de charge définit pour ce produit : \"%s\" (id. : %d)" |
1002 | +msgstr "Il n'y a pas de compte de charge défini pour ce produit : \"%s\" (id. : %d)" |
1003 | |
1004 | #. module: msf_outgoing |
1005 | #: report:packing.list:0 |
1006 | @@ -30674,7 +30676,7 @@ |
1007 | #: code:addons/analytic_distribution_supply/invoice.py:147 |
1008 | #, python-format |
1009 | msgid "There is no expense account defined for this PO line: \"%s\" (id:%d)" |
1010 | -msgstr "There is no expense account defined for this PO line: \"%s\" (id:%d)" |
1011 | +msgstr "Il n'y a pas de compte de charge défini pour cette ligne de BC : \"%s\" (id. : %d)" |
1012 | |
1013 | #. module: msf_doc_import |
1014 | #: view:msf.import.export:0 |
1015 | @@ -44516,12 +44518,6 @@ |
1016 | msgid "Choose day" |
1017 | msgstr "Choisir un jour" |
1018 | |
1019 | -#. module: analytic_distribution |
1020 | -#: code:addons/analytic_distribution/account_commitment.py:161 |
1021 | -#, python-format |
1022 | -msgid "No analytic distribution found for %s %s" |
1023 | -msgstr "Pas de distribution analytique trouvée pour %s %s" |
1024 | - |
1025 | #. modules: sync_so, analytic_distribution_supply, analytic_override, analytic_distribution |
1026 | #: code:addons/analytic_distribution/wizard/analytic_distribution_wizard.py:322 |
1027 | #: code:addons/analytic_distribution/wizard/analytic_distribution_wizard.py:352 |
1028 | @@ -47492,6 +47488,7 @@ |
1029 | #: view:account.bank.statement.line:0 |
1030 | #: view:sale.order.line:0 |
1031 | #: view:free.allocation.wizard:0 |
1032 | +#: view:account.commitment.line:0 |
1033 | msgid "Delete" |
1034 | msgstr "Supprimer" |
1035 | |
1036 | @@ -68111,7 +68108,7 @@ |
1037 | #: code:addons/analytic_distribution_supply/invoice.py:227 |
1038 | #, python-format |
1039 | msgid "No analytic distribution found." |
1040 | -msgstr "Pas de disribution analytique trouvée." |
1041 | +msgstr "Pas de distribution analytique trouvée." |
1042 | |
1043 | #. module: base |
1044 | #: model:res.currency,currency_name:base.CHF |
1045 | @@ -69423,9 +69420,32 @@ |
1046 | |
1047 | #. module: analytic_distribution |
1048 | #: selection:account.commitment,type:0 |
1049 | +#: selection:account.commitment.line,commit_type:0 |
1050 | msgid "Automatic - External supplier" |
1051 | msgstr "Automatique - Fournisseur Externe" |
1052 | |
1053 | +#. module: analytic_distribution |
1054 | +#: selection:account.commitment,type:0 |
1055 | +#: selection:account.commitment.line,commit_type:0 |
1056 | +msgid "Automatic - Intermission" |
1057 | +msgstr "Automatique - Intermission" |
1058 | + |
1059 | +#. module: analytic_distribution |
1060 | +#: selection:account.commitment,type:0 |
1061 | +#: selection:account.commitment.line,commit_type:0 |
1062 | +msgid "Automatic - Intersection" |
1063 | +msgstr "Automatique - Intersection" |
1064 | + |
1065 | +#. module: analytic_distribution |
1066 | +#: view:account.commitment:0 |
1067 | +msgid "Intermission Commitment Voucher" |
1068 | +msgstr "Bon d'Engagement Intermission" |
1069 | + |
1070 | +#. module: analytic_distribution |
1071 | +#: view:account.commitment:0 |
1072 | +msgid "Intersection Commitment Voucher" |
1073 | +msgstr "Bon d'Engagement Intersection" |
1074 | + |
1075 | #. module: procurement |
1076 | #: field:procurement.order,close_move:0 |
1077 | msgid "Close Move at end" |
1078 | @@ -82184,7 +82204,7 @@ |
1079 | msgid "SFTP connection succeeded" |
1080 | msgstr "SFTP connection succeeded" |
1081 | |
1082 | -#. modules: tender_flow, product_nomenclature, product_asset, account_override, product_attributes, register_accounting, product_expiry, procurement_cycle, return_claim, supplier_catalogue, import_data, mission_stock, unifield_setup, stock_forecast, stock_batch_recall, order_types, msf_doc_import, purchase_followup, product, stock_override, stock_schedule, service_purchasing, consumption_calculation, purchase_override, specific_rules, kit, base, product_list, product_manufacturer, procurement_report, threshold_value, purchase, account, msf_outgoing, stock_move_tracking, purchase_allocation_report, procurement_auto, sale, transport_mgmt, procurement, sourcing, msf_audittrail, purchase_msf, stock, sync_so, msf_tools |
1083 | +#. modules: tender_flow, product_nomenclature, product_asset, account_override, product_attributes, register_accounting, product_expiry, procurement_cycle, return_claim, supplier_catalogue, import_data, mission_stock, unifield_setup, stock_forecast, stock_batch_recall, order_types, msf_doc_import, purchase_followup, product, stock_override, stock_schedule, service_purchasing, consumption_calculation, purchase_override, specific_rules, kit, base, product_list, product_manufacturer, procurement_report, threshold_value, purchase, account, msf_outgoing, stock_move_tracking, purchase_allocation_report, procurement_auto, sale, transport_mgmt, procurement, sourcing, msf_audittrail, purchase_msf, stock, sync_so, msf_tools, analytic_distribution |
1084 | #: field:account.analytic.line,product_id:0 |
1085 | #: view:account.entries.report:0 |
1086 | #: field:account.entries.report,product_id:0 |
1087 | @@ -82390,6 +82410,7 @@ |
1088 | #: view:replenishment.segment.line.amc.past_fmc:0 |
1089 | #: view:replenishment.segment.line.min_max_auto_supply.history:0 |
1090 | #: field:view.expired.expiring.stock.lines,product_id:0 |
1091 | +#: field:account.commitment.line,po_line_product_id:0 |
1092 | #, python-format |
1093 | msgid "Product" |
1094 | msgstr "Produit" |
1095 | @@ -87510,7 +87531,7 @@ |
1096 | msgstr "Ceci est utilisé uniquement si vous sélectionnez une localisation de type chainée.\n" |
1097 | "La valeur 'Mouvement automatique' créera un mouvement de stock après le mouvement actuel qui sera validé automatiquement. La valeur 'Opération manuelle', le mouvement de stock devra être validé par un opérateur. La valeur 'Automatique sans étape supplémentaire', la localisation est remplacée sur le mouvement d'origine." |
1098 | |
1099 | -#. modules: msf_budget, sync_client, update_client, kit, base, msf_profile |
1100 | +#. modules: msf_budget, sync_client, update_client, kit, base, msf_profile, analytic_distribution |
1101 | #: view:ir.module.module:0 |
1102 | #: report:ir.module.reference:0 |
1103 | #: view:composition.kit:0 |
1104 | @@ -87528,6 +87549,7 @@ |
1105 | #: field:sync.client.update_to_send,version:0 |
1106 | #: field:sync.version.instance.monitor,version:0 |
1107 | #: field:sync_client.version,name:0 |
1108 | +#: field:account.commitment,version:0 |
1109 | msgid "Version" |
1110 | msgstr "Version" |
1111 | |
1112 | @@ -91254,13 +91276,21 @@ |
1113 | msgid "Purchase Orders Waiting Confirmation" |
1114 | msgstr "Bons de Commandes en attente de Confirmation" |
1115 | |
1116 | -#. modules: purchase, analytic_distribution, purchase_override |
1117 | +#. modules: purchase, analytic_distribution, purchase_override, account_override, register_accounting |
1118 | #: field:account.commitment,line_ids:0 |
1119 | #: view:account.commitment.line:0 |
1120 | +#: field:purchase.order.line,cv_line_ids:0 |
1121 | +#: field:purchase.order.merged.line,cv_line_ids:0 |
1122 | +#: field:account.invoice.line,cv_line_ids:0 |
1123 | +#: field:wizard.account.invoice.line,cv_line_ids:0 |
1124 | +msgid "Commitment Voucher Lines" |
1125 | +msgstr "Lignes de Bon d'Engagement" |
1126 | + |
1127 | +#. modules: purchase, purchase_override |
1128 | #: field:purchase.order.line,commitment_line_ids:0 |
1129 | #: field:purchase.order.merged.line,commitment_line_ids:0 |
1130 | -msgid "Commitment Voucher Lines" |
1131 | -msgstr "Lignes de Bon d'Engagement" |
1132 | +msgid "Commitment Voucher Lines (deprecated)" |
1133 | +msgstr "Lignes de Bon d'Engagement (obsolète)" |
1134 | |
1135 | #. module: sync_client |
1136 | #: code:addons/sync_client/hq_monitor.py:48 |
1137 | @@ -98351,13 +98381,17 @@ |
1138 | msgstr "Type de note" |
1139 | |
1140 | #. modules: purchase, analytic_distribution, msf_doc_import, finance |
1141 | -#: field:account.commitment.line,purchase_order_line_ids:0 |
1142 | #: view:purchase.order.line:0 |
1143 | #: field:wizard.import.po,line_ids:0 |
1144 | #: field:account.invoice.line,purchase_order_line_ids:0 |
1145 | msgid "Purchase Order Lines" |
1146 | msgstr "Lignes Bon de Commande" |
1147 | |
1148 | +#. module: analytic_distribution |
1149 | +#: field:account.commitment.line,purchase_order_line_ids:0 |
1150 | +msgid "Purchase Order Lines (deprecated)" |
1151 | +msgstr "Lignes Bon de Commande (obsolète)" |
1152 | + |
1153 | #. module: specific_rules |
1154 | #: field:unconsistent.stock.report.line,product_bn:0 |
1155 | msgid "BN management" |
1156 | @@ -112379,6 +112413,7 @@ |
1157 | #, python-format |
1158 | msgid "The Pre-Packing List is using a deactivated Delivery Address (%s). Please select another one to be able to process." |
1159 | msgstr "La Liste de Pré-Colisage utilise une Addresse de Livraison désactivée (%s). Veuillez en sélectionner une autre pour pouvoir continuer le traitement." |
1160 | +<<<<<<< TREE |
1161 | |
1162 | #. module: sync_so |
1163 | #: code:addons/sync_so/so_po_common.py:491 |
1164 | @@ -112719,3 +112754,47 @@ |
1165 | #, python-format |
1166 | msgid "Products moved in the last: %s month%s" |
1167 | msgstr "Mouvements de stock dans: %s dernier%s mois" |
1168 | +======= |
1169 | + |
1170 | +#. module: sync_so |
1171 | +#: code:addons/sync_so/so_po_common.py:491 |
1172 | +#, python-format |
1173 | +msgid "Cannot process Document/line due to Product Code %s which does not exist in this instance" |
1174 | +msgstr "Impossible de traiter le Document/la ligne. Le Code Produit %s n'existe pas dans cette instance" |
1175 | + |
1176 | +#. module: analytic_distribution |
1177 | +#: help:account.commitment,version:0 |
1178 | +msgid "Technical field to distinguish old CVs from new ones which have a different behavior." |
1179 | +msgstr "Champ technique pour distinguer les anciens Bons d'Engagement des nouveaux qui ont un comportement différent." |
1180 | + |
1181 | +#. module: analytic_distribution |
1182 | +#: field:account.commitment,display_super_done_button:0 |
1183 | +msgid "Display the button allowing to always set a CV to Done" |
1184 | +msgstr "Afficher le bouton permettant de toujours passer un Bon d'Engagement en Terminé" |
1185 | + |
1186 | +#. module: analytic_distribution |
1187 | +#: field:account.commitment.line,commit_type:0 |
1188 | +msgid "Commitment Voucher Type" |
1189 | +msgstr "Type de Bon d'Engagement" |
1190 | + |
1191 | +#. module: analytic_distribution |
1192 | +#: field:account.commitment.line,po_line_id:0 |
1193 | +#: field:account.commitment.line,po_line_number:0 |
1194 | +msgid "PO Line" |
1195 | +msgstr "Ligne du BC" |
1196 | + |
1197 | +#. module: analytic_distribution |
1198 | +#: view:account.commitment:0 |
1199 | +msgid "Done (for Administrator only)" |
1200 | +msgstr "Terminé (pour l'Administrateur uniquement)" |
1201 | + |
1202 | +#. module: analytic_distribution |
1203 | +#: view:account.commitment:0 |
1204 | +msgid "You are about to set this Commitment Voucher to the state \"Done\". Do you want to proceed?" |
1205 | +msgstr "Vous êtes sur le point de passer ce Bon d'Engagement à l'état \"Terminé\". Voulez-vous continuer ?" |
1206 | + |
1207 | +#. module: analytic_distribution |
1208 | +#: view:account.commitment.line:0 |
1209 | +msgid "Do you really want to delete this line?" |
1210 | +msgstr "Voulez-vous vraiment supprimer cette ligne ?" |
1211 | +>>>>>>> MERGE-SOURCE |
1212 | |
1213 | === modified file 'bin/addons/msf_profile/msf_profile.py' |
1214 | --- bin/addons/msf_profile/msf_profile.py 2021-08-09 18:09:28 +0000 |
1215 | +++ bin/addons/msf_profile/msf_profile.py 2021-08-11 08:19:49 +0000 |
1216 | @@ -53,6 +53,7 @@ |
1217 | 'model': lambda *a: 'patch.scripts', |
1218 | } |
1219 | |
1220 | +<<<<<<< TREE |
1221 | # UF22.0 |
1222 | def us_8805_product_set_archived(self, cr, uid, *a, **b): |
1223 | if self.pool.get('sync_client.version') and self.pool.get('sync.client.entity'): |
1224 | @@ -80,6 +81,18 @@ |
1225 | cr.execute("update internal_request_import set file_to_import=NULL"); |
1226 | return True |
1227 | |
1228 | +======= |
1229 | + # UF22.0 |
1230 | + def us_7449_set_cv_version(self, cr, uid, *a, **b): |
1231 | + """ |
1232 | + Sets the existing Commitment Vouchers in version 1. |
1233 | + """ |
1234 | + if self.pool.get('sync.client.entity'): # existing instances |
1235 | + cr.execute("UPDATE account_commitment SET version = 1") |
1236 | + self._logger.warn('Commitment Vouchers: %s CV(s) set to version 1.', cr.rowcount) |
1237 | + return True |
1238 | + |
1239 | +>>>>>>> MERGE-SOURCE |
1240 | # UF21.1 |
1241 | def us_8810_fake_updates(self, cr, uid, *a, **b): |
1242 | if self.pool.get('sync.client.entity'): |
1243 | |
1244 | === modified file 'bin/addons/purchase/purchase_order.py' |
1245 | --- bin/addons/purchase/purchase_order.py 2021-08-10 16:21:09 +0000 |
1246 | +++ bin/addons/purchase/purchase_order.py 2021-08-11 08:19:49 +0000 |
1247 | @@ -2887,12 +2887,21 @@ |
1248 | ('instance_id', '=', self.pool.get('res.users').browse(cr, uid, uid, context).company_id.instance_id.id) |
1249 | ], limit=1, context=context) |
1250 | |
1251 | + po_partner_type = po.partner_id.partner_type |
1252 | + if po_partner_type == 'external': |
1253 | + cv_type = 'external' |
1254 | + elif po_partner_type == 'section': |
1255 | + cv_type = 'intersection' |
1256 | + elif po_partner_type == 'intermission': |
1257 | + cv_type = 'intermission' |
1258 | + else: |
1259 | + cv_type = 'manual' |
1260 | vals = { |
1261 | 'journal_id': engagement_ids and engagement_ids[0] or False, |
1262 | 'currency_id': po.currency_id and po.currency_id.id or False, |
1263 | 'partner_id': po.partner_id and po.partner_id.id or False, |
1264 | 'purchase_id': po.id or False, |
1265 | - 'type': 'external' if po.partner_id.partner_type == 'external' else 'manual', |
1266 | + 'type': cv_type, |
1267 | } |
1268 | # prepare some values |
1269 | period_ids = get_period_from_date(self, cr, uid, cv_date, context=context) |
1270 | |
1271 | === modified file 'bin/addons/purchase/purchase_order_line.py' |
1272 | --- bin/addons/purchase/purchase_order_line.py 2021-08-10 16:13:59 +0000 |
1273 | +++ bin/addons/purchase/purchase_order_line.py 2021-08-11 08:19:49 +0000 |
1274 | @@ -568,8 +568,12 @@ |
1275 | # finance |
1276 | 'analytic_distribution_id': fields.many2one('analytic.distribution', 'Analytic Distribution'), |
1277 | 'have_analytic_distribution_from_header': fields.function(_have_analytic_distribution_from_header, method=True, type='boolean', string='Header Distrib.?'), |
1278 | + # for CV in version 1 |
1279 | 'commitment_line_ids': fields.many2many('account.commitment.line', 'purchase_line_commitment_rel', 'purchase_id', 'commitment_id', |
1280 | - string="Commitment Voucher Lines", readonly=True), |
1281 | + string="Commitment Voucher Lines (deprecated)", readonly=True), |
1282 | + # for CV starting from version 2 |
1283 | + # note: cv_line_ids is a o2m because of the related m2o on CV lines but it should only contain one CV line |
1284 | + 'cv_line_ids': fields.one2many('account.commitment.line', 'po_line_id', string="Commitment Voucher Lines"), |
1285 | 'analytic_distribution_state': fields.function(_get_distribution_state, method=True, type='selection', |
1286 | selection=[('none', 'None'), ('valid', 'Valid'), ('invalid', 'Invalid')], |
1287 | string="Distribution state", help="Informs from distribution state among 'none', 'valid', 'invalid."), |
1288 | @@ -1295,7 +1299,7 @@ |
1289 | self.pool.get('product.product')._get_restriction_error(cr, uid, [pol.product_id.id], |
1290 | {'partner_id': pol.order_id.partner_id.id}, context=context) |
1291 | |
1292 | - default.update({'state': 'draft', 'move_ids': [], 'invoiced': 0, 'invoice_lines': [], 'commitment_line_ids': [], }) |
1293 | + default.update({'state': 'draft', 'move_ids': [], 'invoiced': 0, 'invoice_lines': [], 'commitment_line_ids': [], 'cv_line_ids': [], }) |
1294 | |
1295 | for field in ['origin', 'move_dest_id', 'original_product', 'original_qty', 'original_price', 'original_uom', 'original_currency_id', 'modification_comment', 'sync_linked_sol', 'created_by_vi_import', 'external_ref']: |
1296 | if field not in default: |
1297 | @@ -1868,14 +1872,17 @@ |
1298 | |
1299 | import_commitments = self.pool.get('unifield.setup.configuration').get_config(cr, uid).import_commitments |
1300 | for pol in self.browse(cr, uid, ids, context=context): |
1301 | - # only create CV for external and ESC partners: |
1302 | - if pol.order_id.partner_id.partner_type not in ['external', 'esc']: |
1303 | + if pol.order_id.partner_id.partner_type == 'internal': |
1304 | return False |
1305 | |
1306 | if pol.order_id.partner_id.partner_type == 'esc' and import_commitments: |
1307 | return False |
1308 | |
1309 | - if pol.order_id.order_type in ['loan', 'in_kind']: |
1310 | + if pol.order_id.order_type in ['loan', 'in_kind', 'donation_st', 'donation_exp']: |
1311 | + return False |
1312 | + |
1313 | + # exclude push flow (FO or FO line created first) |
1314 | + if pol.order_id.push_fo or pol.set_as_sourced_n: |
1315 | return False |
1316 | |
1317 | commitment_voucher_id = self.pool.get('account.commitment').search(cr, uid, [('purchase_id', '=', pol.order_id.id), ('state', '=', 'draft')], context=context) |
1318 | @@ -1886,42 +1893,63 @@ |
1319 | raise osv.except_osv(_('Error'), _('Delivery Confirmed Date is a mandatory field.')) |
1320 | commitment_voucher_id = self.pool.get('purchase.order').create_commitment_voucher_from_po(cr, uid, [pol.order_id.id], cv_date=pol.confirmed_delivery_date, context=context) |
1321 | |
1322 | - # group PO line by account_id: |
1323 | expense_account = pol.account_4_distribution and pol.account_4_distribution.id or False |
1324 | if not expense_account: |
1325 | raise osv.except_osv(_('Error'), _('There is no expense account defined for this line: %s (id:%d)') % (pol.name or '', pol.id)) |
1326 | |
1327 | + # in CV in version 1, PO lines are grouped by account_id. Else 1 PO line generates 1 CV line. |
1328 | + cv_version = self.pool.get('account.commitment').read(cr, uid, commitment_voucher_id, ['version'], context=context)['version'] |
1329 | + cc_lines = [] |
1330 | + ad_header = [] # if filled in, the line itself has no AD but uses the one at header level |
1331 | if pol.analytic_distribution_id: |
1332 | cc_lines = pol.analytic_distribution_id.cost_center_lines |
1333 | - else: |
1334 | + elif cv_version < 2: |
1335 | + # in CV in version 1, if there is no AD on the PO line, the AD at PO header level is used at CV line level |
1336 | cc_lines = pol.order_id.analytic_distribution_id.cost_center_lines |
1337 | + else: |
1338 | + ad_header = pol.order_id.analytic_distribution_id.cost_center_lines |
1339 | |
1340 | - if not cc_lines: |
1341 | + if not cc_lines and not ad_header: |
1342 | raise osv.except_osv(_('Warning'), _('Analytic allocation is mandatory for %s on the line %s for the product %s! It must be added manually.') |
1343 | % (pol.order_id.name, pol.line_number, pol.product_id and pol.product_id.default_code or pol.name or '')) |
1344 | |
1345 | - |
1346 | - commit_line_id = self.pool.get('account.commitment.line').search(cr, uid, [('commit_id', '=', commitment_voucher_id), ('account_id', '=', expense_account)], context=context) |
1347 | - if not commit_line_id: # create new commitment line |
1348 | - distrib_id = self.pool.get('analytic.distribution').create(cr, uid, {}, context=context) |
1349 | - commit_line_id = self.pool.get('account.commitment.line').create(cr, uid, { |
1350 | + new_cv_line = False |
1351 | + if cv_version > 1: |
1352 | + new_cv_line = True |
1353 | + else: |
1354 | + commit_line_id = self.pool.get('account.commitment.line').search(cr, uid, |
1355 | + [('commit_id', '=', commitment_voucher_id), |
1356 | + ('account_id', '=', expense_account)], context=context) |
1357 | + if not commit_line_id: |
1358 | + new_cv_line = True |
1359 | + if new_cv_line: # create new commitment line |
1360 | + if ad_header: # the line has no AD itself, it uses the AD at header level |
1361 | + distrib_id = False |
1362 | + else: |
1363 | + distrib_id = self.pool.get('analytic.distribution').create(cr, uid, {}, context=context) |
1364 | + commit_line_vals = { |
1365 | 'commit_id': commitment_voucher_id, |
1366 | 'account_id': expense_account, |
1367 | 'amount': pol.price_subtotal, |
1368 | 'initial_amount': pol.price_subtotal, |
1369 | - 'purchase_order_line_ids': [(4, pol.id)], |
1370 | 'analytic_distribution_id': distrib_id, |
1371 | - }, context=context) |
1372 | - for aline in cc_lines: |
1373 | - vals = { |
1374 | - 'distribution_id': distrib_id, |
1375 | - 'analytic_id': aline.analytic_id.id, |
1376 | - 'currency_id': pol.order_id.currency_id.id, |
1377 | - 'destination_id': aline.destination_id.id, |
1378 | - 'percentage': aline.percentage, |
1379 | - } |
1380 | - self.pool.get('cost.center.distribution.line').create(cr, uid, vals, context=context) |
1381 | - self.pool.get('analytic.distribution').create_funding_pool_lines(cr, uid, [distrib_id], expense_account, context=context) |
1382 | + } |
1383 | + if cv_version > 1: |
1384 | + commit_line_vals.update({'po_line_id': pol.id, }) |
1385 | + else: |
1386 | + commit_line_vals.update({'purchase_order_line_ids': [(4, pol.id)], }) |
1387 | + commit_line_id = self.pool.get('account.commitment.line').create(cr, uid, commit_line_vals, context=context) |
1388 | + if distrib_id: |
1389 | + for aline in cc_lines: |
1390 | + vals = { |
1391 | + 'distribution_id': distrib_id, |
1392 | + 'analytic_id': aline.analytic_id.id, |
1393 | + 'currency_id': pol.order_id.currency_id.id, |
1394 | + 'destination_id': aline.destination_id.id, |
1395 | + 'percentage': aline.percentage, |
1396 | + } |
1397 | + self.pool.get('cost.center.distribution.line').create(cr, uid, vals, context=context) |
1398 | + self.pool.get('analytic.distribution').create_funding_pool_lines(cr, uid, [distrib_id], expense_account, context=context) |
1399 | |
1400 | else: # update existing commitment line: |
1401 | commit_line_id = commit_line_id[0] |
1402 | |
1403 | === modified file 'bin/addons/purchase/stock.py' |
1404 | --- bin/addons/purchase/stock.py 2020-09-25 15:16:18 +0000 |
1405 | +++ bin/addons/purchase/stock.py 2021-08-11 08:19:49 +0000 |
1406 | @@ -34,58 +34,6 @@ |
1407 | 'purchase_id': False, |
1408 | } |
1409 | |
1410 | - def _get_address_invoice(self, cr, uid, picking): |
1411 | - """ Gets invoice address of a partner |
1412 | - @return {'contact': address, 'invoice': address} for invoice |
1413 | - """ |
1414 | - res = super(stock_picking, self)._get_address_invoice(cr, uid, picking) |
1415 | - if picking.purchase_id: |
1416 | - partner_obj = self.pool.get('res.partner') |
1417 | - partner = picking.purchase_id.partner_id or picking.address_id.partner_id |
1418 | - data = partner_obj.address_get(cr, uid, [partner.id], |
1419 | - ['contact', 'invoice']) |
1420 | - res.update(data) |
1421 | - return res |
1422 | - |
1423 | - def get_currency_id(self, cursor, user, picking): |
1424 | - if picking.purchase_id: |
1425 | - return picking.purchase_id.pricelist_id.currency_id.id |
1426 | - else: |
1427 | - return super(stock_picking, self).get_currency_id(cursor, user, picking) |
1428 | - |
1429 | - def _get_comment_invoice(self, cursor, user, picking): |
1430 | - if picking.purchase_id and picking.purchase_id.notes: |
1431 | - if picking.note: |
1432 | - return picking.note + '\n' + picking.purchase_id.notes |
1433 | - else: |
1434 | - return picking.purchase_id.notes |
1435 | - return super(stock_picking, self)._get_comment_invoice(cursor, user, picking) |
1436 | - |
1437 | - def _get_price_unit_invoice(self, cursor, user, move_line, type): |
1438 | - if move_line.purchase_line_id: |
1439 | - return move_line.purchase_line_id.price_unit |
1440 | - return super(stock_picking, self)._get_price_unit_invoice(cursor, user, move_line, type) |
1441 | - |
1442 | - def _get_discount_invoice(self, cursor, user, move_line): |
1443 | - if move_line.purchase_line_id: |
1444 | - return 0.0 |
1445 | - return super(stock_picking, self)._get_discount_invoice(cursor, user, move_line) |
1446 | - |
1447 | - def _get_taxes_invoice(self, cursor, user, move_line, type): |
1448 | - if move_line.purchase_line_id: |
1449 | - return [x.id for x in move_line.purchase_line_id.taxes_id] |
1450 | - return super(stock_picking, self)._get_taxes_invoice(cursor, user, move_line, type) |
1451 | - |
1452 | - def _get_account_analytic_invoice(self, cursor, user, picking, move_line): |
1453 | - if move_line.purchase_line_id: |
1454 | - return move_line.purchase_line_id.account_analytic_id.id |
1455 | - return super(stock_picking, self)._get_account_analytic_invoice(cursor, user, picking, move_line) |
1456 | - |
1457 | - def _invoice_hook(self, cursor, user, picking, invoice_id): |
1458 | - purchase_obj = self.pool.get('purchase.order') |
1459 | - if picking.purchase_id: |
1460 | - purchase_obj.write(cursor, user, [picking.purchase_id.id], {'invoice_id': invoice_id,}) |
1461 | - return super(stock_picking, self)._invoice_hook(cursor, user, picking, invoice_id) |
1462 | |
1463 | stock_picking() |
1464 | |
1465 | |
1466 | === modified file 'bin/addons/sale/stock.py' |
1467 | --- bin/addons/sale/stock.py 2019-09-18 14:06:52 +0000 |
1468 | +++ bin/addons/sale/stock.py 2021-08-11 08:19:49 +0000 |
1469 | @@ -40,65 +40,6 @@ |
1470 | 'sale_id': False |
1471 | } |
1472 | |
1473 | - def get_currency_id(self, cursor, user, picking): |
1474 | - if picking.sale_id: |
1475 | - return picking.sale_id.pricelist_id.currency_id.id |
1476 | - else: |
1477 | - return super(stock_picking, self).get_currency_id(cursor, user, picking) |
1478 | - |
1479 | - def _get_payment_term(self, cursor, user, picking): |
1480 | - if picking.sale_id and picking.sale_id.payment_term: |
1481 | - return picking.sale_id.payment_term.id |
1482 | - return super(stock_picking, self)._get_payment_term(cursor, user, picking) |
1483 | - |
1484 | - def _get_address_invoice(self, cursor, user, picking): |
1485 | - res = {} |
1486 | - if picking.sale_id: |
1487 | - res['contact'] = picking.sale_id.partner_order_id.id |
1488 | - res['invoice'] = picking.sale_id.partner_invoice_id.id |
1489 | - return res |
1490 | - return super(stock_picking, self)._get_address_invoice(cursor, user, picking) |
1491 | - |
1492 | - def _get_comment_invoice(self, cursor, user, picking): |
1493 | - if picking.note or (picking.sale_id and picking.sale_id.note): |
1494 | - return picking.note or picking.sale_id.note |
1495 | - return super(stock_picking, self)._get_comment_invoice(cursor, user, picking) |
1496 | - |
1497 | - def _get_price_unit_invoice(self, cursor, user, move_line, type): |
1498 | - if move_line.sale_line_id and move_line.sale_line_id.product_id.id == move_line.product_id.id: |
1499 | - uom_id = move_line.product_id.uom_id.id |
1500 | - uos_id = move_line.product_id.uos_id and move_line.product_id.uos_id.id or False |
1501 | - price = move_line.sale_line_id.price_unit |
1502 | - coeff = move_line.product_id.uos_coeff |
1503 | - if uom_id != uos_id and coeff != 0: |
1504 | - price_unit = price / coeff |
1505 | - return price_unit |
1506 | - return move_line.sale_line_id.price_unit |
1507 | - return super(stock_picking, self)._get_price_unit_invoice(cursor, user, move_line, type) |
1508 | - |
1509 | - def _get_discount_invoice(self, cursor, user, move_line): |
1510 | - if move_line.sale_line_id: |
1511 | - return move_line.sale_line_id.discount |
1512 | - return super(stock_picking, self)._get_discount_invoice(cursor, user, move_line) |
1513 | - |
1514 | - def _get_taxes_invoice(self, cursor, user, move_line, type): |
1515 | - if move_line.sale_line_id and move_line.sale_line_id.product_id.id == move_line.product_id.id: |
1516 | - return [x.id for x in move_line.sale_line_id.tax_id] |
1517 | - return super(stock_picking, self)._get_taxes_invoice(cursor, user, move_line, type) |
1518 | - |
1519 | - def _get_account_analytic_invoice(self, cursor, user, picking, move_line): |
1520 | - if picking.sale_id: |
1521 | - return picking.sale_id.project_id.id |
1522 | - return super(stock_picking, self)._get_account_analytic_invoice(cursor, user, picking, move_line) |
1523 | - |
1524 | - def _invoice_hook(self, cursor, user, picking, invoice_id): |
1525 | - sale_obj = self.pool.get('sale.order') |
1526 | - if picking.sale_id: |
1527 | - sale_obj.write(cursor, user, [picking.sale_id.id], { |
1528 | - 'invoice_ids': [(4, invoice_id)], |
1529 | - }) |
1530 | - return super(stock_picking, self)._invoice_hook(cursor, user, picking, invoice_id) |
1531 | - |
1532 | |
1533 | stock_picking() |
1534 | |
1535 | |
1536 | === modified file 'bin/addons/stock/stock.py' |
1537 | --- bin/addons/stock/stock.py 2021-08-09 14:13:09 +0000 |
1538 | +++ bin/addons/stock/stock.py 2021-08-11 08:19:49 +0000 |
1539 | @@ -1193,12 +1193,20 @@ |
1540 | return True |
1541 | |
1542 | def get_currency_id(self, cr, uid, picking): |
1543 | - return False |
1544 | + if picking.sale_id: |
1545 | + return picking.sale_id.pricelist_id.currency_id.id |
1546 | + else: |
1547 | + if picking.purchase_id: |
1548 | + return picking.purchase_id.pricelist_id.currency_id.id |
1549 | + else: |
1550 | + return False |
1551 | |
1552 | def _get_payment_term(self, cr, uid, picking): |
1553 | """ Gets payment term from partner. |
1554 | @return: Payment term |
1555 | """ |
1556 | + if picking.sale_id and picking.sale_id.payment_term: |
1557 | + return picking.sale_id.payment_term.id |
1558 | partner = picking.address_id.partner_id |
1559 | return partner.property_payment_term and partner.property_payment_term.id or False |
1560 | |
1561 | @@ -1206,37 +1214,85 @@ |
1562 | """ Gets invoice address of a partner |
1563 | @return {'contact': address, 'invoice': address} for invoice |
1564 | """ |
1565 | + res = {} |
1566 | partner_obj = self.pool.get('res.partner') |
1567 | + if picking.sale_id: |
1568 | + res['contact'] = picking.sale_id.partner_order_id.id |
1569 | + res['invoice'] = picking.sale_id.partner_invoice_id.id |
1570 | + return res |
1571 | partner = picking.address_id.partner_id |
1572 | - return partner_obj.address_get(cr, uid, [partner.id], |
1573 | - ['contact', 'invoice']) |
1574 | + res = partner_obj.address_get(cr, uid, [partner.id], ['contact', 'invoice']) |
1575 | + if picking.purchase_id: |
1576 | + partner = picking.purchase_id.partner_id or picking.address_id.partner_id |
1577 | + data = partner_obj.address_get(cr, uid, [partner.id], ['contact', 'invoice']) |
1578 | + res.update(data) |
1579 | + return res |
1580 | |
1581 | def _get_comment_invoice(self, cr, uid, picking): |
1582 | """ |
1583 | @return: comment string for invoice |
1584 | """ |
1585 | + if picking.note or (picking.sale_id and picking.sale_id.note): |
1586 | + return picking.note or picking.sale_id.note |
1587 | + if picking.purchase_id and picking.purchase_id.notes: |
1588 | + if picking.note: |
1589 | + return picking.note + '\n' + picking.purchase_id.notes |
1590 | + else: |
1591 | + return picking.purchase_id.notes |
1592 | return picking.note or '' |
1593 | |
1594 | def _get_price_unit_invoice(self, cr, uid, move_line, type, context=None): |
1595 | """ Gets price unit for invoice |
1596 | + Updates the Unit price according to the UoM received and the UoM ordered |
1597 | @param move_line: Stock move lines |
1598 | @param type: Type of invoice |
1599 | @return: The price unit for the move line |
1600 | """ |
1601 | if context is None: |
1602 | context = {} |
1603 | - |
1604 | - if type in ('in_invoice', 'in_refund'): |
1605 | - # Take the user company and pricetype |
1606 | - context['currency_id'] = move_line.company_id.currency_id.id |
1607 | - amount_unit = move_line.product_id.price_get('standard_price', context)[move_line.product_id.id] |
1608 | - return amount_unit |
1609 | - else: |
1610 | - return move_line.product_id.list_price |
1611 | + res = None |
1612 | + if move_line.sale_line_id and move_line.sale_line_id.product_id.id == move_line.product_id.id: |
1613 | + uom_id = move_line.product_id.uom_id.id |
1614 | + uos_id = move_line.product_id.uos_id and move_line.product_id.uos_id.id or False |
1615 | + price = move_line.sale_line_id.price_unit |
1616 | + coeff = move_line.product_id.uos_coeff |
1617 | + if uom_id != uos_id and coeff != 0: |
1618 | + price_unit = price / coeff |
1619 | + res = price_unit |
1620 | + else: |
1621 | + res = move_line.sale_line_id.price_unit |
1622 | + if res is None: |
1623 | + if move_line.purchase_line_id: |
1624 | + res = move_line.purchase_line_id.price_unit |
1625 | + else: |
1626 | + if type in ('in_invoice', 'in_refund'): |
1627 | + # Take the user company and pricetype |
1628 | + context['currency_id'] = move_line.company_id.currency_id.id |
1629 | + amount_unit = move_line.product_id.price_get('standard_price', context)[move_line.product_id.id] |
1630 | + res = amount_unit |
1631 | + else: |
1632 | + res = move_line.product_id.list_price |
1633 | + if type == 'in_refund': |
1634 | + if move_line.picking_id and move_line.picking_id.purchase_id: |
1635 | + po_line_obj = self.pool.get('purchase.order.line') |
1636 | + po_line_id = po_line_obj.search(cr, uid, [('order_id', '=', move_line.picking_id.purchase_id.id), |
1637 | + ('product_id', '=', move_line.product_id.id), |
1638 | + ('state', '!=', 'cancel') |
1639 | + ], limit=1) |
1640 | + if po_line_id: |
1641 | + return po_line_obj.read(cr, uid, po_line_id[0], ['price_unit'])['price_unit'] |
1642 | + if move_line.purchase_line_id: |
1643 | + po_uom_id = move_line.purchase_line_id.product_uom.id |
1644 | + move_uom_id = move_line.product_uom.id |
1645 | + uom_ratio = self.pool.get('product.uom')._compute_price(cr, uid, move_uom_id, 1, po_uom_id) |
1646 | + return res / uom_ratio |
1647 | + return res |
1648 | |
1649 | def _get_discount_invoice(self, cr, uid, move_line): |
1650 | '''Return the discount for the move line''' |
1651 | - return 0.0 |
1652 | + if move_line.sale_line_id: |
1653 | + return move_line.sale_line_id.discount |
1654 | + return 0.0 # including if move_line.purchase_line_id |
1655 | |
1656 | def _get_taxes_invoice(self, cr, uid, move_line, type): |
1657 | """ Gets taxes on invoice |
1658 | @@ -1244,6 +1300,10 @@ |
1659 | @param type: Type of invoice |
1660 | @return: Taxes Ids for the move line |
1661 | """ |
1662 | + if move_line.sale_line_id and move_line.sale_line_id.product_id.id == move_line.product_id.id: |
1663 | + return [x.id for x in move_line.sale_line_id.tax_id] |
1664 | + if move_line.purchase_line_id: |
1665 | + return [x.id for x in move_line.purchase_line_id.taxes_id] |
1666 | if type in ('in_invoice', 'in_refund'): |
1667 | taxes = move_line.product_id.supplier_taxes_id |
1668 | else: |
1669 | @@ -1259,7 +1319,11 @@ |
1670 | else: |
1671 | return map(lambda x: x.id, taxes) |
1672 | |
1673 | - def _get_account_analytic_invoice(self, cr, uid, picking, move_line): |
1674 | + def _get_account_analytic_invoice(self, picking, move_line): |
1675 | + if picking.sale_id: |
1676 | + return picking.sale_id.project_id.id |
1677 | + if move_line.purchase_line_id: |
1678 | + return move_line.purchase_line_id.account_analytic_id.id |
1679 | return False |
1680 | |
1681 | def _invoice_line_hook(self, cr, uid, move_line, invoice_line_id, account_id): |
1682 | @@ -1294,9 +1358,33 @@ |
1683 | return True |
1684 | |
1685 | def _invoice_hook(self, cr, uid, picking, invoice_id): |
1686 | - '''Call after the creation of the invoice''' |
1687 | + """ |
1688 | + Create a link between invoice and purchase_order. |
1689 | + Copy analytic distribution from purchase order to invoice (or from commitment voucher if it exists) |
1690 | + |
1691 | + To call after the creation of the invoice |
1692 | + """ |
1693 | + sale_obj = self.pool.get('sale.order') |
1694 | + purchase_obj = self.pool.get('purchase.order') |
1695 | + if invoice_id and picking: |
1696 | + po_id = picking.purchase_id and picking.purchase_id.id or False |
1697 | + so_id = picking.sale_id and picking.sale_id.id or False |
1698 | + if po_id: |
1699 | + self.pool.get('purchase.order').write(cr, uid, [po_id], {'invoice_ids': [(4, invoice_id)]}) |
1700 | + if so_id: |
1701 | + self.pool.get('sale.order').write(cr, uid, [so_id], {'invoice_ids': [(4, invoice_id)]}) |
1702 | + # Copy analytic distribution from purchase order or commitment voucher (if it exists) or sale order |
1703 | + self.pool.get('account.invoice').fetch_analytic_distribution(cr, uid, [invoice_id]) |
1704 | + if picking.sale_id: |
1705 | + sale_obj.write(cr, uid, [picking.sale_id.id], { |
1706 | + 'invoice_ids': [(4, invoice_id)], |
1707 | + }) |
1708 | + if picking.purchase_id: |
1709 | + purchase_obj.write(cr, uid, [picking.purchase_id.id], {'invoice_id': invoice_id, }) |
1710 | return |
1711 | |
1712 | + # action_invoice_create method has been removed because of the impossibility to retrieve DESTINATION from SO. |
1713 | + |
1714 | def _get_invoice_type(self, pick): |
1715 | src_usage = dest_usage = None |
1716 | inv_type = None |
1717 | @@ -1550,12 +1638,17 @@ |
1718 | else: |
1719 | name = move_line.name |
1720 | |
1721 | + cv_line = move_line and move_line.purchase_line_id and move_line.purchase_line_id.cv_line_ids and \ |
1722 | + move_line.purchase_line_id.cv_line_ids[0] or False |
1723 | + cv_version = cv_line and cv_line.commit_id and cv_line.commit_id.version or 1 |
1724 | if inv_type in ('out_invoice', 'out_refund'): |
1725 | account_id = move_line.product_id.product_tmpl_id.\ |
1726 | property_account_income.id |
1727 | if not account_id: |
1728 | account_id = move_line.product_id.categ_id.\ |
1729 | property_account_income_categ.id |
1730 | + elif cv_version > 1: |
1731 | + account_id = cv_line.account_id.id |
1732 | else: |
1733 | account_id = move_line.product_id.product_tmpl_id.\ |
1734 | property_account_expense.id |
1735 | @@ -1567,14 +1660,15 @@ |
1736 | move_line, inv_type) |
1737 | discount = self._get_discount_invoice(cr, uid, move_line) |
1738 | tax_ids = self._get_taxes_invoice(cr, uid, move_line, inv_type) |
1739 | - account_analytic_id = self._get_account_analytic_invoice(cr, uid, picking, move_line) |
1740 | + account_analytic_id = self._get_account_analytic_invoice(picking, move_line) |
1741 | |
1742 | #set UoS if it's a sale and the picking doesn't have one |
1743 | uos_id = move_line.product_uos and move_line.product_uos.id or False |
1744 | if not uos_id and inv_type in ('out_invoice', 'out_refund'): |
1745 | uos_id = move_line.product_uom.id |
1746 | - account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position, account_id) |
1747 | - invoice_line_id = invoice_line_obj.create(cr, uid, { |
1748 | + if cv_version < 2: |
1749 | + account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position, account_id) |
1750 | + inv_vals = { |
1751 | 'name': name, |
1752 | 'origin': origin, |
1753 | 'invoice_id': invoice_id, |
1754 | @@ -1586,7 +1680,10 @@ |
1755 | 'quantity': move_line.product_uos_qty or move_line.product_qty, |
1756 | 'invoice_line_tax_id': [(6, 0, tax_ids)], |
1757 | 'account_analytic_id': account_analytic_id, |
1758 | - }, context=context) |
1759 | + } |
1760 | + if cv_version > 1: |
1761 | + inv_vals.update({'cv_line_ids': [(4, cv_line.id)],}) |
1762 | + invoice_line_id = invoice_line_obj.create(cr, uid, inv_vals, context=context) |
1763 | self._invoice_line_hook(cr, uid, move_line, invoice_line_id, account_id) |
1764 | |
1765 | if picking.sale_id: |
1766 | @@ -1613,7 +1710,7 @@ |
1767 | tax_ids = sale_line.tax_id |
1768 | tax_ids = map(lambda x: x.id, tax_ids) |
1769 | |
1770 | - account_analytic_id = self._get_account_analytic_invoice(cr, uid, picking, sale_line) |
1771 | + account_analytic_id = self._get_account_analytic_invoice(picking, sale_line) |
1772 | |
1773 | account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, picking.sale_id.partner_id.property_account_position, account_id) |
1774 | invoice_line_id = invoice_line_obj.create(cr, uid, { |
1775 | |
1776 | === modified file 'bin/addons/stock/stock_move.py' |
1777 | === modified file 'bin/addons/stock_override/stock.py' |
1778 | --- bin/addons/stock_override/stock.py 2021-08-10 16:18:43 +0000 |
1779 | +++ bin/addons/stock_override/stock.py 2021-08-11 08:19:49 +0000 |
1780 | @@ -870,29 +870,6 @@ |
1781 | |
1782 | return res |
1783 | |
1784 | - def _get_price_unit_invoice(self, cr, uid, move_line, type): |
1785 | - ''' |
1786 | - Update the Unit price according to the UoM received and the UoM ordered |
1787 | - ''' |
1788 | - res = super(stock_picking, self)._get_price_unit_invoice(cr, uid, move_line, type) |
1789 | - if type == 'in_refund': |
1790 | - if move_line.picking_id and move_line.picking_id.purchase_id: |
1791 | - po_line_obj = self.pool.get('purchase.order.line') |
1792 | - po_line_id = po_line_obj.search(cr, uid, [('order_id', '=', move_line.picking_id.purchase_id.id), |
1793 | - ('product_id', '=', move_line.product_id.id), |
1794 | - ('state', '!=', 'cancel') |
1795 | - ], limit=1) |
1796 | - if po_line_id: |
1797 | - return po_line_obj.read(cr, uid, po_line_id[0], ['price_unit'])['price_unit'] |
1798 | - |
1799 | - if move_line.purchase_line_id: |
1800 | - po_uom_id = move_line.purchase_line_id.product_uom.id |
1801 | - move_uom_id = move_line.product_uom.id |
1802 | - uom_ratio = self.pool.get('product.uom')._compute_price(cr, uid, move_uom_id, 1, po_uom_id) |
1803 | - return res / uom_ratio |
1804 | - |
1805 | - return res |
1806 | - |
1807 | def action_confirm(self, cr, uid, ids, context=None): |
1808 | """ |
1809 | stock.picking: action confirm |
1810 | |
1811 | === modified file 'bin/addons/sync_client/message.py' |
1812 | === modified file 'bin/addons/sync_so/picking.py' |
1813 | === modified file 'bin/addons/sync_so/purchase.py' |
1814 | --- bin/addons/sync_so/purchase.py 2021-08-10 16:18:43 +0000 |
1815 | +++ bin/addons/sync_so/purchase.py 2021-08-11 08:19:49 +0000 |
1816 | @@ -332,7 +332,7 @@ |
1817 | kind = 'update' |
1818 | pol_to_update = [pol_updated] |
1819 | confirmed_sequence = self.pool.get('purchase.order.line.state').get_sequence(cr, uid, [], 'confirmed', context=context) |
1820 | - po_line = self.browse(cr, uid, pol_updated, fields_to_fetch=['state', 'product_qty'], context=context) |
1821 | + po_line = self.browse(cr, uid, pol_updated, fields_to_fetch=['state', 'product_qty', 'price_unit', 'cv_line_ids'], context=context) |
1822 | pol_state = po_line.state |
1823 | if sol_dict['state'] in ['cancel', 'cancel_r']: |
1824 | pol_values['cancelled_by_sync'] = True |
1825 | @@ -340,6 +340,12 @@ |
1826 | # if the state is less than confirmed we update the PO line |
1827 | if debug: |
1828 | logger.info("Write pol id: %s, values: %s" % (pol_to_update, pol_values)) |
1829 | + if po_line.cv_line_ids and po_line.cv_line_ids[0] and po_line.state == 'confirmed' and po_line.product_qty - pol_values.get('product_qty', po_line.product_qty) > 0.01: |
1830 | + # update qty on confirmed po line: update CV line if any |
1831 | + # from_cancel = True : do not trigger wkf transition draft -> open |
1832 | + self.pool.get('account.invoice')._update_commitments_lines(cr, uid, [po_ids[0]], cvl_amount_dic={ |
1833 | + po_line.cv_line_ids[0].id: round((po_line.product_qty - pol_values['product_qty'])*po_line.price_unit, 2) |
1834 | + }, from_cancel=True, context=context) |
1835 | self.pool.get('purchase.order.line').write(cr, uid, pol_to_update, pol_values, context=context) |
1836 | |
1837 | if debug: |
1838 | |
1839 | === modified file 'bin/addons/sync_so/sale.py' |
1840 | === modified file 'bin/addons/sync_so/so_po_common.py' |
1841 | === modified file 'bin/osv/expression.py' |
1842 | --- bin/osv/expression.py 2021-08-09 18:09:28 +0000 |
1843 | +++ bin/osv/expression.py 2021-08-11 08:19:49 +0000 |
1844 | @@ -360,8 +360,13 @@ |
1845 | if field.translate: |
1846 | if operator in ('like', 'ilike', 'not like', 'not ilike'): |
1847 | right = '%%%s%%' % right |
1848 | +<<<<<<< TREE |
1849 | if right and operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): |
1850 | right = right.replace('\\', '\\\\').replace('_', '\\_') |
1851 | +======= |
1852 | + if operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): |
1853 | + right = right.replace('\\', '\\\\').replace('_', '\\_') |
1854 | +>>>>>>> MERGE-SOURCE |
1855 | |
1856 | operator = {'=like':'like','=ilike':'ilike'}.get(operator,operator) |
1857 | |
1858 | @@ -486,8 +491,13 @@ |
1859 | elif left in table._columns: |
1860 | params = table._columns[left]._symbol_set[1](right) |
1861 | |
1862 | +<<<<<<< TREE |
1863 | if params and operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): |
1864 | params = params.replace('\\', '\\\\').replace('_', '\\_') |
1865 | +======= |
1866 | + if operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): |
1867 | + params = params.replace('\\', '\\\\').replace('_', '\\_') |
1868 | +>>>>>>> MERGE-SOURCE |
1869 | if add_null: |
1870 | query = '(%s OR %s IS NULL)' % (query, left) |
1871 |