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 | 100 | line_name = inv_line.get('name', '') | 100 | line_name = inv_line.get('name', '') |
6 | 101 | if not line_name: # required field | 101 | if not line_name: # required field |
7 | 102 | raise osv.except_osv(_('Error'), _("Impossible to retrieve the line description.")) | 102 | raise osv.except_osv(_('Error'), _("Impossible to retrieve the line description.")) |
8 | 103 | uom_id = False | ||
9 | 104 | uom_data = inv_line.get('uos_id', {}) | ||
10 | 105 | if uom_data: | ||
11 | 106 | uom_name = uom_data.get('name', '') | ||
12 | 107 | uom_ids = product_uom_obj.search(cr, uid, [('name', '=', uom_name)], limit=1, context=context) | ||
13 | 108 | if not uom_ids: | ||
14 | 109 | raise osv.except_osv(_('Error'), _("Unit of Measure %s not found.") % uom_name) | ||
15 | 110 | uom_id = uom_ids[0] | ||
16 | 111 | quantity = inv_line.get('quantity', 0.0) | ||
17 | 112 | inv_line_vals = { | ||
18 | 113 | 'invoice_id': inv_id, | ||
19 | 114 | 'name': line_name, | ||
20 | 115 | 'quantity': quantity, | ||
21 | 116 | 'price_unit': inv_line.get('price_unit', 0.0), | ||
22 | 117 | 'discount': inv_line.get('discount', 0.0), | ||
23 | 118 | 'uos_id': uom_id, | ||
24 | 119 | } | ||
25 | 120 | line_account_id = False | ||
26 | 121 | fo_line_dict = inv_line.get('sale_order_line_id') or {} | ||
27 | 122 | if from_supply and inv_linked_po and fo_line_dict.get('sync_local_id'): | ||
28 | 123 | # fill in the AD at line level if applicable | ||
29 | 124 | # search the matching between PO line and invoice line | ||
30 | 125 | 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 | 126 | if po_line_ids: | ||
32 | 127 | matching_po_line = pol_obj.browse(cr, uid, po_line_ids[0], | ||
33 | 128 | fields_to_fetch=['analytic_distribution_id', 'cv_line_ids'], context=context) | ||
34 | 129 | inv_line_vals.update({'order_line_id': matching_po_line.id}) | ||
35 | 130 | if matching_po_line.cv_line_ids: | ||
36 | 131 | inv_line_vals.update({'cv_line_ids': [(6, 0, [cvl.id for cvl in matching_po_line.cv_line_ids])]}) | ||
37 | 132 | # cv_line_ids only contains one CV line: get its account | ||
38 | 133 | line_account_id = matching_po_line.cv_line_ids[0].account_id.id | ||
39 | 134 | po_line_distrib = matching_po_line.analytic_distribution_id | ||
40 | 135 | self._create_analytic_distrib(cr, uid, inv_line_vals, po_line_distrib, context=context) # update inv_line_vals | ||
41 | 103 | product_id = False | 136 | product_id = False |
42 | 104 | product_data = inv_line.get('product_id', {}) | 137 | product_data = inv_line.get('product_id', {}) |
45 | 105 | line_account_id = False | 138 | # for the lines linked to a CV: the CV line account is used (handled above) |
46 | 106 | # for the lines related to a product: use the account of the product / else use the one of the source invoice line | 139 | # for the other lines related to a product: use the account of the product / else use the one of the source invoice line |
47 | 107 | if product_data: | 140 | if product_data: |
48 | 108 | default_code = product_data.get('default_code', '') | 141 | default_code = product_data.get('default_code', '') |
49 | 109 | product_id = so_po_common_obj.get_product_id(cr, uid, product_data, default_code=default_code, context=context) or False | 142 | 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 | 113 | context=context) | 146 | context=context) |
52 | 114 | if not product.active: | 147 | if not product.active: |
53 | 115 | raise osv.except_osv(_('Error'), _("The product %s is inactive.") % product.default_code or '') | 148 | raise osv.except_osv(_('Error'), _("The product %s is inactive.") % product.default_code or '') |
55 | 116 | line_account_id = product.product_tmpl_id.property_account_expense and product.product_tmpl_id.property_account_expense.id | 149 | if not line_account_id: |
56 | 150 | line_account_id = product.product_tmpl_id.property_account_expense and product.product_tmpl_id.property_account_expense.id | ||
57 | 117 | if not line_account_id: | 151 | if not line_account_id: |
58 | 118 | line_account_id = product.categ_id and product.categ_id.property_account_expense_categ and product.categ_id.property_account_expense_categ.id | 152 | line_account_id = product.categ_id and product.categ_id.property_account_expense_categ and product.categ_id.property_account_expense_categ.id |
60 | 119 | else: | 153 | elif not line_account_id: |
61 | 120 | account_code = inv_line.get('account_id', {}).get('code', '') | 154 | account_code = inv_line.get('account_id', {}).get('code', '') |
62 | 121 | if not account_code: | 155 | if not account_code: |
63 | 122 | raise osv.except_osv(_('Error'), _("Impossible to retrieve the account code at line level.")) | 156 | raise osv.except_osv(_('Error'), _("Impossible to retrieve the account code at line level.")) |
64 | @@ -131,34 +165,9 @@ | |||
65 | 131 | if inv_posting_date < line_account.activation_date or \ | 165 | if inv_posting_date < line_account.activation_date or \ |
66 | 132 | (line_account.inactivation_date and inv_posting_date >= line_account.inactivation_date): | 166 | (line_account.inactivation_date and inv_posting_date >= line_account.inactivation_date): |
67 | 133 | raise osv.except_osv(_('Error'), _('The account "%s - %s" is inactive.') % (line_account.code, line_account.name)) | 167 | raise osv.except_osv(_('Error'), _('The account "%s - %s" is inactive.') % (line_account.code, line_account.name)) |
96 | 134 | uom_id = False | 168 | inv_line_vals.update({'account_id': line_account_id, |
97 | 135 | uom_data = inv_line.get('uos_id', {}) | 169 | 'product_id': product_id, |
98 | 136 | if uom_data: | 170 | }) |
71 | 137 | uom_name = uom_data.get('name', '') | ||
72 | 138 | uom_ids = product_uom_obj.search(cr, uid, [('name', '=', uom_name)], limit=1, context=context) | ||
73 | 139 | if not uom_ids: | ||
74 | 140 | raise osv.except_osv(_('Error'), _("Unit of Measure %s not found.") % uom_name) | ||
75 | 141 | uom_id = uom_ids[0] | ||
76 | 142 | quantity = inv_line.get('quantity', 0.0) | ||
77 | 143 | inv_line_vals = { | ||
78 | 144 | 'invoice_id': inv_id, | ||
79 | 145 | 'account_id': line_account_id, | ||
80 | 146 | 'name': line_name, | ||
81 | 147 | 'quantity': quantity, | ||
82 | 148 | 'price_unit': inv_line.get('price_unit', 0.0), | ||
83 | 149 | 'discount': inv_line.get('discount', 0.0), | ||
84 | 150 | 'product_id': product_id, | ||
85 | 151 | 'uos_id': uom_id, | ||
86 | 152 | } | ||
87 | 153 | fo_line_dict = inv_line.get('sale_order_line_id') or {} | ||
88 | 154 | if from_supply and inv_linked_po and fo_line_dict.get('sync_local_id'): | ||
89 | 155 | # fill in the AD at line level if applicable | ||
90 | 156 | # search the matching between PO line and invoice line | ||
91 | 157 | 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 | 158 | if po_line_ids: | ||
93 | 159 | matching_po_line = pol_obj.browse(cr, uid, po_line_ids[0], fields_to_fetch=['analytic_distribution_id'], context=context) | ||
94 | 160 | po_line_distrib = matching_po_line.analytic_distribution_id | ||
95 | 161 | self._create_analytic_distrib(cr, uid, inv_line_vals, po_line_distrib, context=context) # update inv_line_vals | ||
99 | 162 | inv_line_obj.create(cr, uid, inv_line_vals, context=context) | 171 | inv_line_obj.create(cr, uid, inv_line_vals, context=context) |
100 | 163 | 172 | ||
101 | 164 | def create_invoice_from_sync(self, cr, uid, source, invoice_data, context=None): | 173 | def create_invoice_from_sync(self, cr, uid, source, invoice_data, context=None): |
102 | @@ -270,7 +279,9 @@ | |||
103 | 270 | if po_ids: | 279 | if po_ids: |
104 | 271 | po_id = po_ids[0] | 280 | po_id = po_ids[0] |
105 | 272 | if po_id: | 281 | if po_id: |
107 | 273 | vals.update({'main_purchase_id': po_id}) | 282 | vals.update({'main_purchase_id': po_id, |
108 | 283 | 'purchase_ids': [(6, 0, [po_id])], | ||
109 | 284 | }) | ||
110 | 274 | po_fields = ['picking_ids', 'analytic_distribution_id', 'order_line', 'name'] | 285 | po_fields = ['picking_ids', 'analytic_distribution_id', 'order_line', 'name'] |
111 | 275 | po = po_obj.browse(cr, uid, po_id, fields_to_fetch=po_fields, context=context) | 286 | po = po_obj.browse(cr, uid, po_id, fields_to_fetch=po_fields, context=context) |
112 | 276 | po_number = po.name | 287 | po_number = po.name |
113 | 277 | 288 | ||
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 | 1423 | vals = by_account_vals[l.account_id.id] | 1423 | vals = by_account_vals[l.account_id.id] |
119 | 1424 | if l.order_line_id: | 1424 | if l.order_line_id: |
120 | 1425 | vals.setdefault('purchase_order_line_ids', []).append(l.order_line_id.id) | 1425 | vals.setdefault('purchase_order_line_ids', []).append(l.order_line_id.id) |
121 | 1426 | if l.cv_line_ids: | ||
122 | 1427 | vals.setdefault('cv_line_ids', []).extend([cvl.id for cvl in l.cv_line_ids]) | ||
123 | 1426 | else: | 1428 | else: |
124 | 1427 | # new account to merge | 1429 | # new account to merge |
125 | 1428 | vals = vals_template.copy() | 1430 | vals = vals_template.copy() |
126 | @@ -1430,9 +1432,12 @@ | |||
127 | 1430 | '_index_': index, | 1432 | '_index_': index, |
128 | 1431 | 'account_id': l.account_id.id, | 1433 | 'account_id': l.account_id.id, |
129 | 1432 | 'purchase_order_line_ids': [], | 1434 | 'purchase_order_line_ids': [], |
130 | 1435 | 'cv_line_ids': [], | ||
131 | 1433 | }) | 1436 | }) |
132 | 1434 | if l.order_line_id: | 1437 | if l.order_line_id: |
133 | 1435 | vals['purchase_order_line_ids'].append(l.order_line_id.id) | 1438 | vals['purchase_order_line_ids'].append(l.order_line_id.id) |
134 | 1439 | if l.cv_line_ids: | ||
135 | 1440 | vals['cv_line_ids'].extend([cvl.id for cvl in l.cv_line_ids]) | ||
136 | 1436 | index += 1 | 1441 | index += 1 |
137 | 1437 | 1442 | ||
138 | 1438 | ''' | 1443 | ''' |
139 | @@ -1515,6 +1520,8 @@ | |||
140 | 1515 | 1520 | ||
141 | 1516 | vals['purchase_order_line_ids'] = vals['purchase_order_line_ids'] and [(6, 0, vals['purchase_order_line_ids'])] or False | 1521 | vals['purchase_order_line_ids'] = vals['purchase_order_line_ids'] and [(6, 0, vals['purchase_order_line_ids'])] or False |
142 | 1517 | 1522 | ||
143 | 1523 | vals['cv_line_ids'] = vals['cv_line_ids'] and [(6, 0, vals['cv_line_ids'])] or False | ||
144 | 1524 | |||
145 | 1518 | # create merge line | 1525 | # create merge line |
146 | 1519 | vals.update({'merged_line': True}) | 1526 | vals.update({'merged_line': True}) |
147 | 1520 | if not self.pool.get('account.invoice.line').create(cr, uid, | 1527 | if not self.pool.get('account.invoice.line').create(cr, uid, |
148 | @@ -1797,6 +1804,10 @@ | |||
149 | 1797 | ('out_refund', 'Customer Refund'), | 1804 | ('out_refund', 'Customer Refund'), |
150 | 1798 | ('in_refund', 'Supplier Refund')]), | 1805 | ('in_refund', 'Supplier Refund')]), |
151 | 1799 | 'merged_line': fields.boolean(string='Merged Line', help='Line generated by the merging of other lines', readonly=True), | 1806 | 'merged_line': fields.boolean(string='Merged Line', help='Line generated by the merging of other lines', readonly=True), |
152 | 1807 | # - a CV line can be linked to several invoice lines ==> e.g. several partial deliveries, split of invoice lines | ||
153 | 1808 | # - an invoice line can be linked to several CV lines => e.g. merge invoice lines by account | ||
154 | 1809 | 'cv_line_ids': fields.many2many('account.commitment.line', 'inv_line_cv_line_rel', 'inv_line_id', 'cv_line_id', | ||
155 | 1810 | string='Commitment Voucher Lines'), | ||
156 | 1800 | } | 1811 | } |
157 | 1801 | 1812 | ||
158 | 1802 | _defaults = { | 1813 | _defaults = { |
159 | @@ -1943,7 +1954,7 @@ | |||
160 | 1943 | """ | 1954 | """ |
161 | 1944 | Copy an invoice line without its move lines, | 1955 | Copy an invoice line without its move lines, |
162 | 1945 | without the link to a reversed invoice line, | 1956 | without the link to a reversed invoice line, |
164 | 1946 | and without link to PO/FO lines when the duplication is manual | 1957 | and without link to PO/FO/CV lines when the duplication is manual |
165 | 1947 | Reset the merged_line tag. | 1958 | Reset the merged_line tag. |
166 | 1948 | """ | 1959 | """ |
167 | 1949 | if context is None: | 1960 | if context is None: |
168 | @@ -1955,13 +1966,14 @@ | |||
169 | 1955 | 'merged_line': False, | 1966 | 'merged_line': False, |
170 | 1956 | }) | 1967 | }) |
171 | 1957 | # Manual duplication should generate a "manual document not created through the supply workflow" | 1968 | # Manual duplication should generate a "manual document not created through the supply workflow" |
173 | 1958 | # so we don't keep the link to PO/FO at line level | 1969 | # so we don't keep the link to PO/FO/CV at line level |
174 | 1959 | if context.get('from_button') and not context.get('from_split'): | 1970 | if context.get('from_button') and not context.get('from_split'): |
175 | 1960 | default.update({ | 1971 | default.update({ |
176 | 1961 | 'order_line_id': False, | 1972 | 'order_line_id': False, |
177 | 1962 | 'sale_order_line_id': False, | 1973 | 'sale_order_line_id': False, |
178 | 1963 | 'sale_order_lines': False, | 1974 | 'sale_order_lines': False, |
179 | 1964 | 'purchase_order_line_ids': [], | 1975 | 'purchase_order_line_ids': [], |
180 | 1976 | 'cv_line_ids': [(6, 0, [])], | ||
181 | 1965 | }) | 1977 | }) |
182 | 1966 | return super(account_invoice_line, self).copy_data(cr, uid, inv_id, default, context) | 1978 | return super(account_invoice_line, self).copy_data(cr, uid, inv_id, default, context) |
183 | 1967 | 1979 | ||
184 | 1968 | 1980 | ||
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 | 67 | res.append(cvl.commit_id.id) | 67 | res.append(cvl.commit_id.id) |
190 | 68 | return res | 68 | return res |
191 | 69 | 69 | ||
192 | 70 | def get_cv_type(self, cr, uid, context=None): | ||
193 | 71 | """ | ||
194 | 72 | Returns the list of possible types for the Commitment Vouchers | ||
195 | 73 | """ | ||
196 | 74 | return [('manual', 'Manual'), | ||
197 | 75 | ('external', 'Automatic - External supplier'), | ||
198 | 76 | ('esc', 'Manual - ESC supplier'), | ||
199 | 77 | ('intermission', 'Automatic - Intermission'), | ||
200 | 78 | ('intersection', 'Automatic - Intersection'), | ||
201 | 79 | ] | ||
202 | 80 | |||
203 | 81 | def get_current_cv_version(self, cr, uid, context=None): | ||
204 | 82 | """ | ||
205 | 83 | Version 2 since US-7449 | ||
206 | 84 | """ | ||
207 | 85 | return 2 | ||
208 | 86 | |||
209 | 87 | def _display_super_done_button(self, cr, uid, ids, name, arg, context=None): | ||
210 | 88 | """ | ||
211 | 89 | For now the "Super" Done button, which allows to always set a CV to Done whatever its state and origin, | ||
212 | 90 | is visible only by the Admin user. It is displayed only when the standard Done button isn't usable. | ||
213 | 91 | """ | ||
214 | 92 | if context is None: | ||
215 | 93 | context = {} | ||
216 | 94 | if isinstance(ids, (int, long)): | ||
217 | 95 | ids = [ids] | ||
218 | 96 | res = {} | ||
219 | 97 | for cv in self.read(cr, uid, ids, ['state', 'type'], context=context): | ||
220 | 98 | other_done_button_usable = cv['state'] == 'open' and cv['type'] not in ('external', 'intermission', 'intersection') | ||
221 | 99 | res[cv['id']] = not other_done_button_usable and uid == 1 and cv['state'] != 'done' | ||
222 | 100 | return res | ||
223 | 101 | |||
224 | 70 | _columns = { | 102 | _columns = { |
225 | 71 | 'journal_id': fields.many2one('account.analytic.journal', string="Journal", readonly=True, required=True), | 103 | 'journal_id': fields.many2one('account.analytic.journal', string="Journal", readonly=True, required=True), |
226 | 72 | 'name': fields.char(string="Number", size=64, readonly=True, required=True), | 104 | 'name': fields.char(string="Number", size=64, readonly=True, required=True), |
227 | @@ -81,16 +113,22 @@ | |||
228 | 81 | 'account.commitment.line': (_get_cv, ['amount'],10), | 113 | 'account.commitment.line': (_get_cv, ['amount'],10), |
229 | 82 | }), | 114 | }), |
230 | 83 | 'analytic_distribution_id': fields.many2one('analytic.distribution', string="Analytic distribution"), | 115 | 'analytic_distribution_id': fields.many2one('analytic.distribution', string="Analytic distribution"), |
232 | 84 | 'type': fields.selection([('manual', 'Manual'), ('external', 'Automatic - External supplier'), ('esc', 'Manual - ESC supplier')], string="Type", readonly=True), | 116 | 'type': fields.selection(get_cv_type, string="Type", readonly=True), |
233 | 85 | 'notes': fields.text(string="Comment"), | 117 | 'notes': fields.text(string="Comment"), |
234 | 86 | 'purchase_id': fields.many2one('purchase.order', string="Source document", readonly=True), | 118 | 'purchase_id': fields.many2one('purchase.order', string="Source document", readonly=True), |
235 | 87 | 'description': fields.char(string="Description", size=256), | 119 | 'description': fields.char(string="Description", size=256), |
236 | 120 | 'version': fields.integer('Version', required=True, | ||
237 | 121 | help="Technical field to distinguish old CVs from new ones which have a different behavior."), | ||
238 | 122 | 'display_super_done_button': fields.function(_display_super_done_button, method=True, type='boolean', | ||
239 | 123 | store=False, invisible=True, | ||
240 | 124 | string='Display the button allowing to always set a CV to Done'), | ||
241 | 88 | } | 125 | } |
242 | 89 | 126 | ||
243 | 90 | _defaults = { | 127 | _defaults = { |
244 | 91 | 'state': lambda *a: 'draft', | 128 | 'state': lambda *a: 'draft', |
245 | 92 | 'date': lambda *a: strftime('%Y-%m-%d'), | 129 | 'date': lambda *a: strftime('%Y-%m-%d'), |
246 | 93 | 'type': lambda *a: 'manual', | 130 | 'type': lambda *a: 'manual', |
247 | 131 | 'version': get_current_cv_version, | ||
248 | 94 | 'journal_id': lambda s, cr, uid, c: s.pool.get('account.analytic.journal').search(cr, uid, [('type', '=', 'engagement'), | 132 | 'journal_id': lambda s, cr, uid, c: s.pool.get('account.analytic.journal').search(cr, uid, [('type', '=', 'engagement'), |
249 | 95 | ('instance_id', '=', s.pool.get('res.users').browse(cr, uid, uid, c).company_id.instance_id.id)], limit=1, context=c)[0] | 133 | ('instance_id', '=', s.pool.get('res.users').browse(cr, uid, uid, c).company_id.instance_id.id)], limit=1, context=c)[0] |
250 | 96 | } | 134 | } |
251 | @@ -181,29 +219,30 @@ | |||
252 | 181 | fctal_currency = user_obj.browse(cr, uid, uid, fields_to_fetch=['company_id'], context=context).company_id.currency_id.id | 219 | fctal_currency = user_obj.browse(cr, uid, uid, fields_to_fetch=['company_id'], context=context).company_id.currency_id.id |
253 | 182 | for cl in c.line_ids: | 220 | for cl in c.line_ids: |
254 | 183 | # Verify that date is compatible with all analytic account from distribution | 221 | # Verify that date is compatible with all analytic account from distribution |
255 | 222 | distrib = False | ||
256 | 184 | if cl.analytic_distribution_id: | 223 | if cl.analytic_distribution_id: |
257 | 185 | distrib = cl.analytic_distribution_id | 224 | distrib = cl.analytic_distribution_id |
258 | 186 | elif cl.commit_id and cl.commit_id.analytic_distribution_id: | 225 | elif cl.commit_id and cl.commit_id.analytic_distribution_id: |
259 | 187 | distrib = cl.commit_id.analytic_distribution_id | 226 | distrib = cl.commit_id.analytic_distribution_id |
279 | 188 | else: | 227 | if distrib: |
280 | 189 | raise osv.except_osv(_('Warning'), _('No analytic distribution found for %s %s') % (cl.account_id.code, cl.initial_amount)) | 228 | for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, |
281 | 190 | for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]: | 229 | distrib.free_1_lines, distrib.free_2_lines]: |
282 | 191 | for distrib_line in distrib_lines: | 230 | for distrib_line in distrib_lines: |
283 | 192 | if distrib_line.analytic_id and \ | 231 | if distrib_line.analytic_id and \ |
284 | 193 | (distrib_line.analytic_id.date_start and date < distrib_line.analytic_id.date_start or | 232 | (distrib_line.analytic_id.date_start and date < distrib_line.analytic_id.date_start or |
285 | 194 | distrib_line.analytic_id.date and date >= distrib_line.analytic_id.date): | 233 | distrib_line.analytic_id.date and date >= distrib_line.analytic_id.date): |
286 | 195 | raise osv.except_osv(_('Error'), _('The analytic account %s is not active for given date.') % | 234 | raise osv.except_osv(_('Error'), _('The analytic account %s is not active for given date.') % |
287 | 196 | (distrib_line.analytic_id.name,)) | 235 | (distrib_line.analytic_id.name,)) |
288 | 197 | dest_cc_tuples = set() # check each Dest/CC combination only once | 236 | dest_cc_tuples = set() # check each Dest/CC combination only once |
289 | 198 | for distrib_cc_l in distrib.cost_center_lines: | 237 | for distrib_cc_l in distrib.cost_center_lines: |
290 | 199 | if distrib_cc_l.analytic_id: # non mandatory field | 238 | if distrib_cc_l.analytic_id: # non mandatory field |
291 | 200 | dest_cc_tuples.add((distrib_cc_l.destination_id, distrib_cc_l.analytic_id)) | 239 | dest_cc_tuples.add((distrib_cc_l.destination_id, distrib_cc_l.analytic_id)) |
292 | 201 | for distrib_fp_l in distrib.funding_pool_lines: | 240 | for distrib_fp_l in distrib.funding_pool_lines: |
293 | 202 | dest_cc_tuples.add((distrib_fp_l.destination_id, distrib_fp_l.cost_center_id)) | 241 | dest_cc_tuples.add((distrib_fp_l.destination_id, distrib_fp_l.cost_center_id)) |
294 | 203 | for dest, cc in dest_cc_tuples: | 242 | for dest, cc in dest_cc_tuples: |
295 | 204 | if dest_cc_link_obj.is_inactive_dcl(cr, uid, dest.id, cc.id, date, context=context): | 243 | if dest_cc_link_obj.is_inactive_dcl(cr, uid, dest.id, cc.id, date, context=context): |
296 | 205 | raise osv.except_osv(_('Error'), _("The combination \"%s - %s\" is not active at this date: %s") % | 244 | raise osv.except_osv(_('Error'), _("The combination \"%s - %s\" is not active at this date: %s") % |
297 | 206 | (dest.code or '', cc.code or '', date)) | 245 | (dest.code or '', cc.code or '', date)) |
298 | 207 | # update the dates and fctal amounts of the related analytic lines | 246 | # update the dates and fctal amounts of the related analytic lines |
299 | 208 | context.update({'currency_date': date}) # same date used for doc, posting and source date of all lines | 247 | context.update({'currency_date': date}) # same date used for doc, posting and source date of all lines |
300 | 209 | for aal in cl.analytic_lines: | 248 | for aal in cl.analytic_lines: |
301 | @@ -233,6 +272,7 @@ | |||
302 | 233 | default.update({ | 272 | default.update({ |
303 | 234 | 'name': self.pool.get('ir.sequence').get(cr, uid, 'account.commitment'), | 273 | 'name': self.pool.get('ir.sequence').get(cr, uid, 'account.commitment'), |
304 | 235 | 'state': 'draft', | 274 | 'state': 'draft', |
305 | 275 | 'version': self.get_current_cv_version(cr, uid, context=context), | ||
306 | 236 | }) | 276 | }) |
307 | 237 | # Default method | 277 | # Default method |
308 | 238 | res = super(account_commitment, self).copy(cr, uid, c_id, default, context) | 278 | res = super(account_commitment, self).copy(cr, uid, c_id, default, context) |
309 | @@ -312,6 +352,12 @@ | |||
310 | 312 | 'context': context, | 352 | 'context': context, |
311 | 313 | } | 353 | } |
312 | 314 | 354 | ||
313 | 355 | def button_analytic_distribution_2(self, cr, uid, ids, context=None): | ||
314 | 356 | """ | ||
315 | 357 | This is just an alias for button_analytic_distribution (used to have different names and attrs on both buttons) | ||
316 | 358 | """ | ||
317 | 359 | return self.button_analytic_distribution(cr, uid, ids, context=context) | ||
318 | 360 | |||
319 | 315 | def button_reset_distribution(self, cr, uid, ids, context=None): | 361 | def button_reset_distribution(self, cr, uid, ids, context=None): |
320 | 316 | """ | 362 | """ |
321 | 317 | Reset analytic distribution on all commitment lines. | 363 | Reset analytic distribution on all commitment lines. |
322 | @@ -444,10 +490,11 @@ | |||
323 | 444 | # Search analytic lines that have commitment line ids | 490 | # Search analytic lines that have commitment line ids |
324 | 445 | 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) | 491 | 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 | 446 | # Delete them | 492 | # Delete them |
327 | 447 | res = self.pool.get('account.analytic.line').unlink(cr, uid, search_ids, context=context) | 493 | if search_ids: |
328 | 494 | res = self.pool.get('account.analytic.line').unlink(cr, uid, search_ids, context=context) | ||
329 | 495 | if not res: | ||
330 | 496 | raise osv.except_osv(_('Error'), _('An error occurred on engagement lines deletion.')) | ||
331 | 448 | # And finally update commitment voucher state and lines amount | 497 | # And finally update commitment voucher state and lines amount |
332 | 449 | if not res: | ||
333 | 450 | raise osv.except_osv(_('Error'), _('An error occurred on engagement lines deletion.')) | ||
334 | 451 | self.pool.get('account.commitment.line').write(cr, uid, [x.id for x in c.line_ids], {'amount': 0}, context=context) | 498 | self.pool.get('account.commitment.line').write(cr, uid, [x.id for x in c.line_ids], {'amount': 0}, context=context) |
335 | 452 | self.write(cr, uid, [c.id], {'state':'done'}, context=context) | 499 | self.write(cr, uid, [c.id], {'state':'done'}, context=context) |
336 | 453 | return True | 500 | return True |
337 | @@ -457,7 +504,7 @@ | |||
338 | 457 | class account_commitment_line(osv.osv): | 504 | class account_commitment_line(osv.osv): |
339 | 458 | _name = 'account.commitment.line' | 505 | _name = 'account.commitment.line' |
340 | 459 | _description = "Account Commitment Voucher Line" | 506 | _description = "Account Commitment Voucher Line" |
342 | 460 | _order = "id desc" | 507 | _order = "po_line_id, id desc" |
343 | 461 | _rec_name = 'account_id' | 508 | _rec_name = 'account_id' |
344 | 462 | _trace = True | 509 | _trace = True |
345 | 463 | 510 | ||
346 | @@ -497,6 +544,12 @@ | |||
347 | 497 | res[co.id] = False | 544 | res[co.id] = False |
348 | 498 | return res | 545 | return res |
349 | 499 | 546 | ||
350 | 547 | def get_cv_type(self, cr, uid, context=None): | ||
351 | 548 | """ | ||
352 | 549 | Gets the possible CV types | ||
353 | 550 | """ | ||
354 | 551 | return self.pool.get('account.commitment').get_cv_type(cr, uid, context) | ||
355 | 552 | |||
356 | 500 | _columns = { | 553 | _columns = { |
357 | 501 | 'account_id': fields.many2one('account.account', string="Account", required=True), | 554 | 'account_id': fields.many2one('account.account', string="Account", required=True), |
358 | 502 | 'amount': fields.float(string="Amount left", digits_compute=dp.get_precision('Account'), required=False), | 555 | 'amount': fields.float(string="Amount left", digits_compute=dp.get_precision('Account'), required=False), |
359 | @@ -504,6 +557,8 @@ | |||
360 | 504 | 'commit_id': fields.many2one('account.commitment', string="Commitment Voucher", on_delete="cascade"), | 557 | 'commit_id': fields.many2one('account.commitment', string="Commitment Voucher", on_delete="cascade"), |
361 | 505 | 'commit_number': fields.related('commit_id', 'name', type='char', size=64, | 558 | 'commit_number': fields.related('commit_id', 'name', type='char', size=64, |
362 | 506 | readonly=True, store=False, string="Commitment Voucher Number"), | 559 | readonly=True, store=False, string="Commitment Voucher Number"), |
363 | 560 | 'commit_type': fields.related('commit_id', 'type', string="Commitment Voucher Type", type='selection', readonly=True, | ||
364 | 561 | store=False, invisible=True, selection=get_cv_type, write_relate=False), | ||
365 | 507 | 'analytic_distribution_id': fields.many2one('analytic.distribution', string="Analytic distribution"), | 562 | 'analytic_distribution_id': fields.many2one('analytic.distribution', string="Analytic distribution"), |
366 | 508 | 'analytic_distribution_state': fields.function(_get_distribution_state, method=True, type='selection', | 563 | 'analytic_distribution_state': fields.function(_get_distribution_state, method=True, type='selection', |
367 | 509 | selection=[('none', 'None'), ('valid', 'Valid'), ('invalid', 'Invalid')], | 564 | selection=[('none', 'None'), ('valid', 'Valid'), ('invalid', 'Invalid')], |
368 | @@ -513,8 +568,15 @@ | |||
369 | 513 | 'analytic_lines': fields.one2many('account.analytic.line', 'commitment_line_id', string="Analytic Lines"), | 568 | 'analytic_lines': fields.one2many('account.analytic.line', 'commitment_line_id', string="Analytic Lines"), |
370 | 514 | 'first': fields.boolean(string="Is not created?", help="Useful for onchange method for views. Should be False after line creation.", | 569 | 'first': fields.boolean(string="Is not created?", help="Useful for onchange method for views. Should be False after line creation.", |
371 | 515 | readonly=True), | 570 | readonly=True), |
372 | 571 | # for CV in version 1 | ||
373 | 516 | 'purchase_order_line_ids': fields.many2many('purchase.order.line', 'purchase_line_commitment_rel', 'commitment_id', 'purchase_id', | 572 | 'purchase_order_line_ids': fields.many2many('purchase.order.line', 'purchase_line_commitment_rel', 'commitment_id', 'purchase_id', |
375 | 517 | string="Purchase Order Lines", readonly=True), | 573 | string="Purchase Order Lines (deprecated)", readonly=True), |
376 | 574 | # for CV starting from version 2 | ||
377 | 575 | 'po_line_id': fields.many2one('purchase.order.line', "PO Line"), | ||
378 | 576 | 'po_line_product_id': fields.related('po_line_id', 'product_id', type='many2one', relation='product.product', | ||
379 | 577 | string="Product", readonly=True, store=True, write_relate=False), | ||
380 | 578 | 'po_line_number': fields.related('po_line_id', 'line_number', type='integer_null', string="PO Line", readonly=True, | ||
381 | 579 | store=True, write_relate=False, _fnct_migrate=lambda *a: True), | ||
382 | 518 | } | 580 | } |
383 | 519 | 581 | ||
384 | 520 | _defaults = { | 582 | _defaults = { |
385 | @@ -656,6 +718,19 @@ | |||
386 | 656 | self.update_analytic_lines(cr, uid, [line.id], vals.get('amount'), account_id, context=context) | 718 | self.update_analytic_lines(cr, uid, [line.id], vals.get('amount'), account_id, context=context) |
387 | 657 | return super(account_commitment_line, self).write(cr, uid, ids, vals, context={}) | 719 | return super(account_commitment_line, self).write(cr, uid, ids, vals, context={}) |
388 | 658 | 720 | ||
389 | 721 | def copy_data(self, cr, uid, cv_line_id, default=None, context=None): | ||
390 | 722 | """ | ||
391 | 723 | Duplicates a CV line: resets the link to PO line | ||
392 | 724 | """ | ||
393 | 725 | if context is None: | ||
394 | 726 | context = {} | ||
395 | 727 | if default is None: | ||
396 | 728 | default = {} | ||
397 | 729 | default.update({ | ||
398 | 730 | 'po_line_id': False, | ||
399 | 731 | }) | ||
400 | 732 | return super(account_commitment_line, self).copy_data(cr, uid, cv_line_id, default, context) | ||
401 | 733 | |||
402 | 659 | def button_analytic_distribution(self, cr, uid, ids, context=None): | 734 | def button_analytic_distribution(self, cr, uid, ids, context=None): |
403 | 660 | """ | 735 | """ |
404 | 661 | Launch analytic distribution wizard on a commitment voucher line | 736 | Launch analytic distribution wizard on a commitment voucher line |
405 | 662 | 737 | ||
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 | 28 | <button name="button_reset_distribution" string="Reset AD at line level" type="object" icon="gtk-undelete" colspan="4" states="draft"/> | 28 | <button name="button_reset_distribution" string="Reset AD at line level" type="object" icon="gtk-undelete" colspan="4" states="draft"/> |
411 | 29 | </group> | 29 | </group> |
412 | 30 | <group colspan="8" col="8" attrs="{'invisible': [('analytic_distribution_id', '!=', False)]}"> | 30 | <group colspan="8" col="8" attrs="{'invisible': [('analytic_distribution_id', '!=', False)]}"> |
414 | 31 | <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-emblem-important" context="context" colspan="4" attrs="{'invisible': [('analytic_distribution_id', '!=', False)]}"/> | 31 | <button name="button_analytic_distribution_2" string="Analytical Distribution" |
415 | 32 | type="object" icon="terp-emblem-important" context="context" colspan="4" | ||
416 | 33 | attrs="{'readonly': [('state', '=', 'open'), ('type', '=', 'manual')]}"/> | ||
417 | 32 | <button name="button_reset_distribution" string="Reset AD at line level" type="object" icon="gtk-undelete" colspan="4" states="draft"/> | 34 | <button name="button_reset_distribution" string="Reset AD at line level" type="object" icon="gtk-undelete" colspan="4" states="draft"/> |
418 | 33 | </group> | 35 | </group> |
419 | 34 | <field name="analytic_distribution_id" invisible="1"/> | 36 | <field name="analytic_distribution_id" invisible="1"/> |
420 | 35 | <notebook colspan="4"> | 37 | <notebook colspan="4"> |
421 | 36 | <page string="Commitment voucher lines"> | 38 | <page string="Commitment voucher lines"> |
423 | 37 | <field name="line_ids" nolabel="1" colspan="4" attrs="{'readonly': ['|', ('state', '=', 'done'), ('type', '=', 'external')]}"/> | 39 | <field name="line_ids" nolabel="1" colspan="4" |
424 | 40 | attrs="{'readonly': ['|', ('state', '=', 'done'), | ||
425 | 41 | '&', | ||
426 | 42 | ('type', 'in', ['external', 'intermission', 'intersection']), | ||
427 | 43 | ('state', '!=', 'draft')]}" | ||
428 | 44 | /> | ||
429 | 38 | </page> | 45 | </page> |
430 | 39 | </notebook> | 46 | </notebook> |
431 | 40 | <field name="notes" colspan="4"/> | 47 | <field name="notes" colspan="4"/> |
432 | 41 | <group col="6" colspan="4"> | 48 | <group col="6" colspan="4"> |
433 | 42 | <button name="button_compute" string="Compute total" icon="gtk-execute" colspan="2"/> | 49 | <button name="button_compute" string="Compute total" icon="gtk-execute" colspan="2"/> |
434 | 43 | <button name="commitment_open" string="Validate" icon="terp-camera_test" states="draft" colspan="2"/> | 50 | <button name="commitment_open" string="Validate" icon="terp-camera_test" states="draft" colspan="2"/> |
436 | 44 | <button name="commitment_validate" string="Done" icon="terp-gtk-go-back-rtl" states="open" colspan="2" attrs="{'readonly': [('type', '=', 'external')]}"/> | 51 | <field name="display_super_done_button"/> |
437 | 52 | <button name="commitment_validate" string="Done" icon="terp-gtk-go-back-rtl" colspan="2" | ||
438 | 53 | attrs="{'invisible': ['|', ('state', '!=', 'open'), ('display_super_done_button', '=', True)], | ||
439 | 54 | 'readonly': [('type', 'in', ['external', 'intermission', 'intersection'])]}" | ||
440 | 55 | /> | ||
441 | 56 | <!-- button which allows to always set a CV to Done (whatever its state and origin) --> | ||
442 | 57 | <button name="commitment_always_validate" string="Done (for Administrator only)" | ||
443 | 58 | icon="terp-gtk-go-back-rtl" colspan="6" | ||
444 | 59 | confirm='You are about to set this Commitment Voucher to the state "Done". Do you want to proceed?' | ||
445 | 60 | attrs="{'invisible': [('display_super_done_button', '=', False)]}"/> | ||
446 | 45 | </group> | 61 | </group> |
447 | 46 | <field name="state"/> | 62 | <field name="state"/> |
448 | 47 | <field name="total"/> | 63 | <field name="total"/> |
449 | @@ -73,14 +89,32 @@ | |||
450 | 73 | <field name="model">account.commitment.line</field> | 89 | <field name="model">account.commitment.line</field> |
451 | 74 | <field name="type">tree</field> | 90 | <field name="type">tree</field> |
452 | 75 | <field name="arch" type="xml"> | 91 | <field name="arch" type="xml"> |
454 | 76 | <tree string="Commitment Voucher Lines" editable="top" colors="red:analytic_distribution_state == 'invalid'"> | 92 | <!-- display the "New" button or not depending on the state and type of the CV itself --> |
455 | 93 | <tree string="Commitment Voucher Lines" editable="top" colors="red:analytic_distribution_state == 'invalid'" | ||
456 | 94 | button_attrs="{'invisible': ['|', ('state', '=', 'done'), ('type', 'in', ['external', 'intermission', 'intersection'])]}" | ||
457 | 95 | hide_delete_button="1" | ||
458 | 96 | > | ||
459 | 97 | <field name="commit_type"/> | ||
460 | 98 | <field name="po_line_product_id"/> | ||
461 | 99 | <field name="po_line_number"/> | ||
462 | 77 | <field name="account_id" domain="[('restricted_area', '=', 'commitment_lines')]"/> | 100 | <field name="account_id" domain="[('restricted_area', '=', 'commitment_lines')]"/> |
466 | 78 | <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-stock_symbol-selection" context="context"/> | 101 | <button name="button_analytic_distribution" string="Analytical Distribution" type="object" |
467 | 79 | <field name="analytic_distribution_state"/> | 102 | icon="terp-stock_symbol-selection" context="context" |
468 | 80 | <field name="have_analytic_distribution_from_header"/> | 103 | /> |
469 | 104 | <field name="analytic_distribution_state" readonly="1"/> | ||
470 | 105 | <field name="have_analytic_distribution_from_header" readonly="1"/> | ||
471 | 81 | <field name="first" invisible="1"/> | 106 | <field name="first" invisible="1"/> |
474 | 82 | <field name="initial_amount" on_change="onchange_initial_amount(first, initial_amount)"/> | 107 | <field name="initial_amount" on_change="onchange_initial_amount(first, initial_amount)" |
475 | 83 | <field name="amount" attrs="{'readonly': [('first', '=', True)]}"/> | 108 | attrs="{'readonly': [('commit_type', 'in', ['external', 'intermission', 'intersection'])]}" |
476 | 109 | /> | ||
477 | 110 | <field name="amount" | ||
478 | 111 | attrs="{'readonly': ['|', ('first', '=', True), ('commit_type', 'in', ['external', 'intermission', 'intersection'])]}" | ||
479 | 112 | /> | ||
480 | 113 | <!-- display the "Delete" button or not depending on the state and type of the CV itself --> | ||
481 | 114 | <button name="unlink" string="Delete" icon="gtk-del" type="object" | ||
482 | 115 | attrs="{'invisible': ['|', ('state', '=', 'done'), ('type', 'in', ['external', 'intermission', 'intersection'])]}" | ||
483 | 116 | confirm="Do you really want to delete this line?" | ||
484 | 117 | /> | ||
485 | 84 | </tree> | 118 | </tree> |
486 | 85 | </field> | 119 | </field> |
487 | 86 | </record> | 120 | </record> |
488 | @@ -95,6 +129,8 @@ | |||
489 | 95 | <group col='6' colspan='4'> | 129 | <group col='6' colspan='4'> |
490 | 96 | <filter icon="terp-tools" string="Manual" domain="[('type','=','manual')]" help="Manual Commitment Voucher"/> | 130 | <filter icon="terp-tools" string="Manual" domain="[('type','=','manual')]" help="Manual Commitment Voucher"/> |
491 | 97 | <filter icon="gtk-quit" string="External" domain="[('type','=','external')]" help="External Commitment Voucher"/> | 131 | <filter icon="gtk-quit" string="External" domain="[('type','=','external')]" help="External Commitment Voucher"/> |
492 | 132 | <filter icon="gtk-refresh" string="Intersection" domain="[('type', '=', 'intersection')]" help="Intersection Commitment Voucher"/> | ||
493 | 133 | <filter icon="gtk-ok" string="Intermission" domain="[('type', '=', 'intermission')]" help="Intermission Commitment Voucher"/> | ||
494 | 98 | <filter icon="terp-partner" string="ESC" domain="[('type','=','esc')]" help="ESC Commitment Voucher"/> | 134 | <filter icon="terp-partner" string="ESC" domain="[('type','=','esc')]" help="ESC Commitment Voucher"/> |
495 | 99 | <separator orientation="vertical"/> | 135 | <separator orientation="vertical"/> |
496 | 100 | <filter icon="terp-document-new" string="Draft" domain="[('state','=','draft')]" help="Commitment Voucher in Draft state" name="draft"/> | 136 | <filter icon="terp-document-new" string="Draft" domain="[('state','=','draft')]" help="Commitment Voucher in Draft state" name="draft"/> |
497 | 101 | 137 | ||
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 | 34 | <record id="commit_t1" model="workflow.transition"> | 34 | <record id="commit_t1" model="workflow.transition"> |
503 | 35 | <field name="act_from" ref="act_draft"/> | 35 | <field name="act_from" ref="act_draft"/> |
504 | 36 | <field name="act_to" ref="act_open"/> | 36 | <field name="act_to" ref="act_open"/> |
505 | 37 | <field name="sequence">10</field> | ||
506 | 37 | <field name="signal">commitment_open</field> | 38 | <field name="signal">commitment_open</field> |
507 | 38 | </record> | 39 | </record> |
508 | 39 | 40 | ||
509 | 40 | <record id="commit_t2" model="workflow.transition"> | 41 | <record id="commit_t2" model="workflow.transition"> |
510 | 41 | <field name="act_from" ref="act_open"/> | 42 | <field name="act_from" ref="act_open"/> |
511 | 42 | <field name="act_to" ref="act_done"/> | 43 | <field name="act_to" ref="act_done"/> |
512 | 44 | <field name="sequence">10</field> | ||
513 | 43 | <field name="signal">commitment_validate</field> | 45 | <field name="signal">commitment_validate</field> |
514 | 44 | </record> | 46 | </record> |
515 | 45 | 47 | ||
516 | 48 | <record id="commit_t3" model="workflow.transition"> | ||
517 | 49 | <field name="act_from" ref="act_open"/> | ||
518 | 50 | <field name="act_to" ref="act_done"/> | ||
519 | 51 | <field name="sequence">15</field> <!-- (sequence, act_from) must be unique --> | ||
520 | 52 | <field name="signal">commitment_always_validate</field> | ||
521 | 53 | </record> | ||
522 | 54 | |||
523 | 55 | <record id="commit_t4" model="workflow.transition"> | ||
524 | 56 | <field name="act_from" ref="act_draft"/> | ||
525 | 57 | <field name="act_to" ref="act_done"/> | ||
526 | 58 | <field name="sequence">15</field> <!-- (sequence, act_from) must be unique --> | ||
527 | 59 | <field name="signal">commitment_always_validate</field> | ||
528 | 60 | </record> | ||
529 | 61 | |||
530 | 46 | </data> | 62 | </data> |
531 | 47 | </openerp> | 63 | </openerp> |
532 | 48 | 64 | ||
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 | 26 | from osv import fields | 26 | from osv import fields |
538 | 27 | from tools.translate import _ | 27 | from tools.translate import _ |
539 | 28 | from base import currency_date | 28 | from base import currency_date |
540 | 29 | import netsvc | ||
541 | 29 | 30 | ||
542 | 30 | 31 | ||
543 | 31 | class account_invoice_line(osv.osv): | 32 | class account_invoice_line(osv.osv): |
544 | @@ -93,20 +94,27 @@ | |||
545 | 93 | self.pool.get('account.invoice').write(cr, uid, [inv.id], {'analytic_distribution_id': new_distrib_id,}) | 94 | self.pool.get('account.invoice').write(cr, uid, [inv.id], {'analytic_distribution_id': new_distrib_id,}) |
546 | 94 | # Then set distribution on invoice line regarding purchase order line distribution | 95 | # Then set distribution on invoice line regarding purchase order line distribution |
547 | 95 | for invl in inv.invoice_line: | 96 | for invl in inv.invoice_line: |
549 | 96 | if invl.order_line_id: | 97 | line_distrib_id = False |
550 | 98 | if invl.cv_line_ids: | ||
551 | 99 | # CV STARTING FROM VERSION 2 | ||
552 | 100 | # the first CV line found is used since there can be only one at this step (merging lines by account could | ||
553 | 101 | # generate an invoice line linked to several CV lines but this action can only be done later in the process) | ||
554 | 102 | line_distrib_id = invl.cv_line_ids[0].analytic_distribution_id and invl.cv_line_ids[0].analytic_distribution_id.id or False | ||
555 | 103 | elif invl.order_line_id: | ||
556 | 97 | # Fetch PO line analytic distribution or nothing (that implies it take those from PO) | 104 | # Fetch PO line analytic distribution or nothing (that implies it take those from PO) |
558 | 98 | distrib_id = invl.order_line_id.analytic_distribution_id and invl.order_line_id.analytic_distribution_id.id or False | 105 | line_distrib_id = invl.order_line_id.analytic_distribution_id and invl.order_line_id.analytic_distribution_id.id or False |
559 | 99 | # Attempt to fetch commitment line analytic distribution or commitment voucher analytic distribution or default distrib_id | 106 | # Attempt to fetch commitment line analytic distribution or commitment voucher analytic distribution or default distrib_id |
560 | 107 | # CV IN VERSION 1 | ||
561 | 100 | if invl.order_line_id.commitment_line_ids: | 108 | if invl.order_line_id.commitment_line_ids: |
571 | 101 | distrib_id = invl.order_line_id.commitment_line_ids[0].analytic_distribution_id \ | 109 | line_distrib_id = invl.order_line_id.commitment_line_ids[0].analytic_distribution_id \ |
572 | 102 | and invl.order_line_id.commitment_line_ids[0].analytic_distribution_id.id or distrib_id | 110 | and invl.order_line_id.commitment_line_ids[0].analytic_distribution_id.id or line_distrib_id |
573 | 103 | if distrib_id: | 111 | if line_distrib_id: |
574 | 104 | new_invl_distrib_id = ana_obj.copy(cr, uid, distrib_id, {}) | 112 | new_invl_distrib_id = ana_obj.copy(cr, uid, line_distrib_id, {}) |
575 | 105 | if not new_invl_distrib_id: | 113 | if not new_invl_distrib_id: |
576 | 106 | raise osv.except_osv(_('Error'), _('An error occurred for analytic distribution copy for invoice.')) | 114 | raise osv.except_osv(_('Error'), _('An error occurred for analytic distribution copy for invoice.')) |
577 | 107 | # create default funding pool lines | 115 | # create default funding pool lines |
578 | 108 | ana_obj.create_funding_pool_lines(cr, uid, [new_invl_distrib_id], invl.account_id.id) | 116 | ana_obj.create_funding_pool_lines(cr, uid, [new_invl_distrib_id], invl.account_id.id) |
579 | 109 | invl_obj.write(cr, uid, [invl.id], {'analytic_distribution_id': new_invl_distrib_id}) | 117 | invl_obj.write(cr, uid, [invl.id], {'analytic_distribution_id': new_invl_distrib_id}) |
580 | 110 | # Fetch SO line analytic distribution | 118 | # Fetch SO line analytic distribution |
581 | 111 | # sol AD copy moved into _invoice_line_hook | 119 | # sol AD copy moved into _invoice_line_hook |
582 | 112 | return True | 120 | return True |
583 | @@ -124,7 +132,8 @@ | |||
584 | 124 | 132 | ||
585 | 125 | # Browse invoices | 133 | # Browse invoices |
586 | 126 | for inv in self.browse(cr, uid, ids, context=context): | 134 | for inv in self.browse(cr, uid, ids, context=context): |
588 | 127 | grouped_invl = {} | 135 | grouped_invl_by_acc = {} |
589 | 136 | grouped_invl_by_cvl = {} | ||
590 | 128 | 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) | 137 | 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 | 129 | if not co_ids: | 138 | if not co_ids: |
592 | 130 | continue | 139 | continue |
593 | @@ -133,27 +142,50 @@ | |||
594 | 133 | # Do not take invoice line that have no order_line_id (so that are not linked to a purchase order line) | 142 | # Do not take invoice line that have no order_line_id (so that are not linked to a purchase order line) |
595 | 134 | if not invl.order_line_id and not inv.is_merged_by_account: | 143 | if not invl.order_line_id and not inv.is_merged_by_account: |
596 | 135 | continue | 144 | continue |
615 | 136 | 145 | # exclude push flow | |
616 | 137 | # Fetch purchase order line account | 146 | if invl.order_line_id and (invl.order_line_id.order_id.push_fo or invl.order_line_id.set_as_sourced_n): |
617 | 138 | if inv.is_merged_by_account: | 147 | continue |
618 | 139 | if not invl.account_id: | 148 | old_cv_version = True |
619 | 140 | continue | 149 | # CV STARTING FROM VERSION 2 |
620 | 141 | # US-357: lines without product (get directly account) | 150 | amount_to_subtract = invl.price_subtotal or 0.0 |
621 | 142 | a = invl.account_id.id | 151 | for cv_line in invl.cv_line_ids: |
622 | 143 | else: | 152 | old_cv_version = False # the field cv_line_ids exist for CVs starting from version 2 |
623 | 144 | pol = invl.order_line_id | 153 | if abs(amount_to_subtract) <= 10**-3: |
624 | 145 | a = self._get_expense_account(cr, uid, pol, context=context) | 154 | break |
625 | 146 | if pol.product_id and not a: | 155 | cvl_amount_left = cv_line.amount or 0.0 |
626 | 147 | 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)) | 156 | if cvl_amount_left: |
627 | 148 | elif not a: | 157 | if cv_line.id not in grouped_invl_by_cvl: |
628 | 149 | raise osv.except_osv(_('Error !'), _('There is no expense account defined for this PO line: "%s" (id:%d)') % (pol.line_number, pol.id)) | 158 | grouped_invl_by_cvl[cv_line.id] = 0 |
629 | 150 | if a not in grouped_invl: | 159 | if amount_to_subtract >= cvl_amount_left: |
630 | 151 | grouped_invl[a] = 0 | 160 | grouped_invl_by_cvl[cv_line.id] += cvl_amount_left |
631 | 152 | 161 | amount_to_subtract -= cvl_amount_left | |
632 | 153 | grouped_invl[a] += invl.price_subtotal | 162 | else: |
633 | 163 | grouped_invl_by_cvl[cv_line.id] += amount_to_subtract | ||
634 | 164 | amount_to_subtract = 0 | ||
635 | 165 | # CV IN VERSION 1 | ||
636 | 166 | if old_cv_version: | ||
637 | 167 | # Fetch purchase order line account | ||
638 | 168 | if inv.is_merged_by_account: | ||
639 | 169 | if not invl.account_id: | ||
640 | 170 | continue | ||
641 | 171 | # US-357: lines without product (get directly account) | ||
642 | 172 | a = invl.account_id.id | ||
643 | 173 | else: | ||
644 | 174 | pol = invl.order_line_id | ||
645 | 175 | a = self._get_expense_account(cr, uid, pol, context=context) | ||
646 | 176 | if pol.product_id and not a: | ||
647 | 177 | raise osv.except_osv(_('Error !'), _('There is no expense account defined for this product: "%s" (id:%d)') % | ||
648 | 178 | (pol.product_id.name, pol.product_id.id)) | ||
649 | 179 | elif not a: | ||
650 | 180 | raise osv.except_osv(_('Error !'), _('There is no expense account defined for this PO line: "%s" (id:%d)') % | ||
651 | 181 | (pol.line_number, pol.id)) | ||
652 | 182 | if a not in grouped_invl_by_acc: | ||
653 | 183 | grouped_invl_by_acc[a] = 0 | ||
654 | 184 | grouped_invl_by_acc[a] += invl.price_subtotal | ||
655 | 154 | 185 | ||
656 | 155 | po_ids = [x.id for x in inv.purchase_ids] | 186 | po_ids = [x.id for x in inv.purchase_ids] |
658 | 156 | self._update_commitments_lines(cr, uid, po_ids, grouped_invl, from_cancel=False, context=context) | 187 | self._update_commitments_lines(cr, uid, po_ids, account_amount_dic=grouped_invl_by_acc, |
659 | 188 | cvl_amount_dic=grouped_invl_by_cvl, from_cancel=False, context=context) | ||
660 | 157 | 189 | ||
661 | 158 | return True | 190 | return True |
662 | 159 | 191 | ||
663 | @@ -170,38 +202,51 @@ | |||
664 | 170 | 202 | ||
665 | 171 | return account_id | 203 | return account_id |
666 | 172 | 204 | ||
668 | 173 | def _update_commitments_lines(self, cr, uid, po_ids, account_amount_dic, from_cancel=False, context=None): | 205 | def _update_commitments_lines(self, cr, uid, po_ids, account_amount_dic=None, cvl_amount_dic=None, from_cancel=False, context=None): |
669 | 174 | """ | 206 | """ |
670 | 175 | po_ids: list of PO ids | 207 | po_ids: list of PO ids |
671 | 176 | account_amount_dic: dict, keys are G/L account_id, values are amount to deduce | 208 | account_amount_dic: dict, keys are G/L account_id, values are amount to deduce |
672 | 177 | 209 | ||
673 | 178 | 210 | ||
674 | 179 | """ | 211 | """ |
676 | 180 | if not po_ids or not account_amount_dic: | 212 | if not po_ids or not account_amount_dic and not cvl_amount_dic: |
677 | 181 | return True | 213 | return True |
678 | 182 | 214 | ||
679 | 183 | if context is None: | 215 | if context is None: |
680 | 184 | context = {} | 216 | context = {} |
681 | 217 | if account_amount_dic is None: | ||
682 | 218 | account_amount_dic = {} | ||
683 | 219 | if cvl_amount_dic is None: | ||
684 | 220 | cvl_amount_dic = {} | ||
685 | 221 | wf_service = netsvc.LocalService("workflow") | ||
686 | 185 | 222 | ||
687 | 186 | # po is state=cancel on last IN cancel | 223 | # po is state=cancel on last IN cancel |
688 | 187 | company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id | 224 | company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id |
690 | 188 | 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 | 225 | # avoids empty lists so that the SQL request can be executed |
691 | 226 | account_list = account_amount_dic.keys() or [0] | ||
692 | 227 | cvl_list = cvl_amount_dic.keys() or [0] | ||
693 | 228 | cr.execute('''select l.id, l.account_id, l.commit_id, c.state, l.amount, l.analytic_distribution_id, c.analytic_distribution_id, | ||
694 | 229 | c.id, c.currency_id, c.type, c.version from | ||
695 | 189 | account_commitment_line l, account_commitment c | 230 | account_commitment_line l, account_commitment c |
696 | 190 | where l.commit_id = c.id and | 231 | where l.commit_id = c.id and |
697 | 191 | l.amount > 0 and | 232 | l.amount > 0 and |
698 | 192 | c.purchase_id in %s and | 233 | c.purchase_id in %s and |
700 | 193 | l.account_id in %s and | 234 | (c.version < 2 and l.account_id in %s or c.version >= 2 and l.id in %s) and |
701 | 194 | c.state in ('open', 'draft') | 235 | c.state in ('open', 'draft') |
702 | 195 | order by c.date asc | 236 | order by c.date asc |
704 | 196 | ''', (tuple(po_ids), tuple(account_amount_dic.keys())) | 237 | ''', (tuple(po_ids), tuple(account_list), tuple(cvl_list)) |
705 | 197 | ) | 238 | ) |
706 | 198 | # sort all cv lines by account / cv date | 239 | # sort all cv lines by account / cv date |
708 | 199 | cv_info = {} | 240 | account_info = {} |
709 | 241 | cvl_info = {} | ||
710 | 200 | auto_cv = True | 242 | auto_cv = True |
711 | 201 | for cv in cr.fetchall(): | 243 | for cv in cr.fetchall(): |
715 | 202 | if cv[1] not in cv_info: | 244 | if cv[10] < 2: |
716 | 203 | cv_info[cv[1]] = [] | 245 | # CV IN VERSION 1 |
717 | 204 | cv_info[cv[1]].append(cv) | 246 | account_info.setdefault(cv[1], []).append(cv) # key = account id |
718 | 247 | else: | ||
719 | 248 | # CV STARTING FROM VERSION 2 | ||
720 | 249 | cvl_info.setdefault(cv[0], []).append(cv) # key = CV line id | ||
721 | 205 | if cv[9] == 'manual': | 250 | if cv[9] == 'manual': |
722 | 206 | auto_cv = False | 251 | auto_cv = False |
723 | 207 | 252 | ||
724 | @@ -210,76 +255,74 @@ | |||
725 | 210 | cv_to_close = {} | 255 | cv_to_close = {} |
726 | 211 | 256 | ||
727 | 212 | # deduce amount on oldest cv lines | 257 | # deduce amount on oldest cv lines |
759 | 213 | for account in account_amount_dic.keys(): | 258 | # NOTE: account_amount_dic is for CV in version 1 based on accounts (acc), |
760 | 214 | if account not in cv_info: | 259 | # cvl_amount_dic is for CV from version 2 based on CV lines (cvl) |
761 | 215 | continue | 260 | for c_type in ("acc", "cvl"): |
762 | 216 | for cv_line in cv_info[account]: | 261 | if c_type == "acc": |
763 | 217 | if cv_line[3] == 'draft' and cv_line[2] not in draft_opened and not from_cancel: | 262 | cv_info = account_info.copy() |
764 | 218 | draft_opened.append(cv_line[2]) | 263 | amount_dic = account_amount_dic.copy() |
765 | 219 | # If Commitment voucher in draft state we change it to 'validated' without using workflow and engagement lines generation | 264 | else: |
766 | 220 | # NB: This permits to avoid modification on commitment voucher when receiving some goods | 265 | cv_info = cvl_info.copy() |
767 | 221 | self.pool.get('account.commitment').write(cr, uid, [cv_line[2]], {'state': 'open'}, context=context) | 266 | amount_dic = cvl_amount_dic.copy() |
768 | 222 | 267 | for k in amount_dic.keys(): # account id or CV line id | |
769 | 223 | if cv_line[4] - account_amount_dic[account] > 0.001: | 268 | if k not in cv_info: |
770 | 224 | # update amount left on CV line | 269 | continue |
771 | 225 | amount_left = cv_line[4] - account_amount_dic[account] | 270 | for cv_line in cv_info[k]: |
772 | 226 | self.pool.get('account.commitment.line').write(cr, uid, [cv_line[0]], {'amount': amount_left}, context=context) | 271 | if cv_line[3] == 'draft' and cv_line[2] not in draft_opened and not from_cancel: |
773 | 227 | 272 | draft_opened.append(cv_line[2]) | |
774 | 228 | # update AAL | 273 | # Change Draft CV to Validated State, in order to avoid CV modification when receiving some goods. |
775 | 229 | distrib_id = cv_line[5] or cv_line[6] | 274 | # The workflow is used so that all the engagement lines are generated, even those which are not |
776 | 230 | if not distrib_id: | 275 | # affected by the current update (e.g. partial reception + SI validation on one in 2 products). |
777 | 231 | raise osv.except_osv(_('Error'), _('No analytic distribution found.')) | 276 | wf_service.trg_validate(uid, 'account.commitment', cv_line[2], 'commitment_open', cr) |
778 | 232 | 277 | ||
779 | 233 | # Browse distribution | 278 | if cv_line[4] - amount_dic[k] > 0.001: |
780 | 234 | distrib = self.pool.get('analytic.distribution').browse(cr, uid, [distrib_id], context=context)[0] | 279 | # update amount left on CV line |
781 | 235 | engagement_lines = distrib.analytic_lines | 280 | amount_left = cv_line[4] - amount_dic[k] |
782 | 236 | for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]: | 281 | self.pool.get('account.commitment.line').write(cr, uid, [cv_line[0]], {'amount': amount_left}, context=context) |
783 | 237 | for distrib_line in distrib_lines: | 282 | # update AAL |
784 | 238 | vals = { | 283 | distrib_id = cv_line[5] or cv_line[6] |
785 | 239 | 'account_id': distrib_line.analytic_id.id, | 284 | if not distrib_id: |
786 | 240 | 'general_account_id': account, | 285 | raise osv.except_osv(_('Error'), _('No analytic distribution found.')) |
787 | 241 | } | 286 | # Browse distribution |
788 | 242 | if distrib_line._name == 'funding.pool.distribution.line': | 287 | distrib = self.pool.get('analytic.distribution').browse(cr, uid, [distrib_id], context=context)[0] |
789 | 243 | vals.update({'cost_center_id': distrib_line.cost_center_id and distrib_line.cost_center_id.id or False,}) | 288 | engagement_lines = distrib.analytic_lines |
790 | 289 | for distrib_line in distrib.funding_pool_lines: | ||
791 | 244 | # Browse engagement lines to found out matching elements | 290 | # Browse engagement lines to found out matching elements |
793 | 245 | for i in range(0,len(engagement_lines)): | 291 | for i in range(0, len(engagement_lines)): |
794 | 246 | if engagement_lines[i]: | 292 | if engagement_lines[i]: |
795 | 247 | eng_line = engagement_lines[i] | 293 | eng_line = engagement_lines[i] |
831 | 248 | cmp_vals = { | 294 | # restrict to the current CV line only |
832 | 249 | 'account_id': eng_line.account_id.id, | 295 | if eng_line.commitment_line_id and eng_line.commitment_line_id.id == cv_line[0]: |
833 | 250 | 'general_account_id': eng_line.general_account_id.id, | 296 | eng_line_distrib_id = eng_line.distrib_line_id and \ |
834 | 251 | } | 297 | eng_line.distrib_line_id._name == 'funding.pool.distribution.line' and \ |
835 | 252 | if eng_line.cost_center_id: | 298 | eng_line.distrib_line_id.id or False |
836 | 253 | cmp_vals.update({'cost_center_id': eng_line.cost_center_id.id}) | 299 | # in case of an AD with several lines, several AJIs are linked to the same CV line: |
837 | 254 | if cmp_vals == vals: | 300 | # the comparison is used to decrement the right one |
838 | 255 | # Update analytic line with new amount | 301 | if eng_line_distrib_id == distrib_line.id: |
839 | 256 | anal_amount = (distrib_line.percentage * amount_left) / 100 | 302 | # Update analytic line with new amount |
840 | 257 | curr_date = currency_date.get_date(self, cr, eng_line.document_date, eng_line.date, | 303 | anal_amount = (distrib_line.percentage * amount_left) / 100 |
841 | 258 | source_date=eng_line.source_date) | 304 | curr_date = currency_date.get_date(self, cr, eng_line.document_date, eng_line.date, |
842 | 259 | context.update({'currency_date': curr_date}) | 305 | source_date=eng_line.source_date) |
843 | 260 | amount = -1 * self.pool.get('res.currency').compute(cr, uid, cv_line[8], company_currency, | 306 | context.update({'currency_date': curr_date}) |
844 | 261 | anal_amount, round=False, context=context) | 307 | amount = -1 * self.pool.get('res.currency').compute(cr, uid, cv_line[8], company_currency, |
845 | 262 | 308 | anal_amount, round=False, context=context) | |
846 | 263 | # write new amount to corresponding engagement line | 309 | # write new amount to corresponding engagement line |
847 | 264 | self.pool.get('account.analytic.line').write(cr, uid, [eng_line.id], | 310 | self.pool.get('account.analytic.line').write(cr, uid, [eng_line.id], |
848 | 265 | {'amount': amount, 'amount_currency': -1 * anal_amount}, context=context) | 311 | {'amount': amount, |
849 | 266 | 312 | 'amount_currency': -1 * anal_amount}, context=context) | |
850 | 267 | # check next G/L account | 313 | # check next G/L account or CV line |
851 | 268 | break | 314 | break |
852 | 269 | 315 | cv_to_close[cv_line[2]] = True | |
853 | 270 | cv_to_close[cv_line[2]] = True | 316 | eng_ids = self.pool.get('account.analytic.line').search(cr, uid, [('commitment_line_id', '=', cv_line[0])], context=context) |
854 | 271 | eng_ids = self.pool.get('account.analytic.line').search(cr, uid, [('commitment_line_id', '=', cv_line[0])], context=context) | 317 | if eng_ids: |
855 | 272 | if eng_ids: | 318 | self.pool.get('account.analytic.line').unlink(cr, uid, eng_ids, context=context) |
856 | 273 | self.pool.get('account.analytic.line').unlink(cr, uid, eng_ids, context=context) | 319 | self.pool.get('account.commitment.line').write(cr, uid, [cv_line[0]], {'amount': 0.0}, context=context) |
857 | 274 | self.pool.get('account.commitment.line').write(cr, uid, [cv_line[0]], {'amount': 0.0}, context=context) | 320 | if abs(cv_line[4] - amount_dic[k]) < 0.001: |
858 | 275 | if abs(cv_line[4] - account_amount_dic[account]) < 0.001: | 321 | # check next G/L account or CV line |
859 | 276 | # check next G/L account | 322 | break |
860 | 277 | break | 323 | amount_dic[k] -= cv_line[4] |
861 | 278 | 324 | ||
862 | 279 | # check next CV on this account | 325 | if auto_cv and from_cancel and from_cancel is not True: |
828 | 280 | account_amount_dic[account] -= cv_line[4] | ||
829 | 281 | |||
830 | 282 | if auto_cv and from_cancel: | ||
863 | 283 | # we cancel the last IN from PO and no draft invoice exist | 326 | # we cancel the last IN from PO and no draft invoice exist |
864 | 284 | if not self.pool.get('account.invoice').search_exist(cr, uid, [('purchase_ids', 'in', po_ids), ('state', '=', 'draft')], context=context): | 327 | if not self.pool.get('account.invoice').search_exist(cr, uid, [('purchase_ids', 'in', po_ids), ('state', '=', 'draft')], context=context): |
865 | 285 | dpo_ids = self.pool.get('purchase.order').search(cr, uid, [('id', 'in', po_ids), ('po_version', '!=', 1), ('order_type', '=', 'direct')], context=context) | 328 | dpo_ids = self.pool.get('purchase.order').search(cr, uid, [('id', 'in', po_ids), ('po_version', '!=', 1), ('order_type', '=', 'direct')], context=context) |
866 | 286 | 329 | ||
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 | 23 | 23 | ||
872 | 24 | from osv import osv | 24 | from osv import osv |
873 | 25 | 25 | ||
874 | 26 | class stock_picking(osv.osv): | ||
875 | 27 | _name = 'stock.picking' | ||
876 | 28 | _inherit = 'stock.picking' | ||
877 | 29 | |||
878 | 30 | |||
879 | 31 | def _invoice_hook(self, cr, uid, picking, invoice_id): | ||
880 | 32 | """ | ||
881 | 33 | Create a link between invoice and purchase_order. | ||
882 | 34 | Copy analytic distribution from purchase order to invoice (or from commitment voucher if exists) | ||
883 | 35 | """ | ||
884 | 36 | if invoice_id and picking: | ||
885 | 37 | po_id = picking.purchase_id and picking.purchase_id.id or False | ||
886 | 38 | so_id = picking.sale_id and picking.sale_id.id or False | ||
887 | 39 | if po_id: | ||
888 | 40 | self.pool.get('purchase.order').write(cr, uid, [po_id], {'invoice_ids': [(4, invoice_id)]}) | ||
889 | 41 | if so_id: | ||
890 | 42 | self.pool.get('sale.order').write(cr, uid, [so_id], {'invoice_ids': [(4, invoice_id)]}) | ||
891 | 43 | # Copy analytic distribution from purchase order or commitment voucher (if exists) or sale order | ||
892 | 44 | self.pool.get('account.invoice').fetch_analytic_distribution(cr, uid, [invoice_id]) | ||
893 | 45 | return super(stock_picking, self)._invoice_hook(cr, uid, picking, invoice_id) | ||
894 | 46 | |||
895 | 47 | # action_invoice_create method have been removed because of impossibility to retrieve DESTINATION from SO. | ||
896 | 48 | |||
897 | 49 | stock_picking() | ||
898 | 50 | 26 | ||
899 | 51 | class stock_move(osv.osv): | 27 | class stock_move(osv.osv): |
900 | 52 | _name = 'stock.move' | 28 | _name = 'stock.move' |
901 | @@ -64,6 +40,7 @@ | |||
902 | 64 | 40 | ||
903 | 65 | inv_obj = self.pool.get('account.invoice') | 41 | inv_obj = self.pool.get('account.invoice') |
904 | 66 | account_amount = {} | 42 | account_amount = {} |
905 | 43 | cvl_amount = {} | ||
906 | 67 | po_ids = {} | 44 | po_ids = {} |
907 | 68 | for move in self.browse(cr, uid, ids, context=context): | 45 | for move in self.browse(cr, uid, ids, context=context): |
908 | 69 | # Fetch all necessary elements | 46 | # Fetch all necessary elements |
909 | @@ -80,14 +57,21 @@ | |||
910 | 80 | continue | 57 | continue |
911 | 81 | 58 | ||
912 | 82 | po_ids[move.purchase_line_id.order_id.id] = True | 59 | po_ids[move.purchase_line_id.order_id.id] = True |
921 | 83 | account_id = inv_obj._get_expense_account(cr, uid, move.purchase_line_id, context=context) | 60 | cv_line = move.purchase_line_id.cv_line_ids and move.purchase_line_id.cv_line_ids[0] or False |
922 | 84 | if account_id: | 61 | cv_version = cv_line and cv_line.commit_id and cv_line.commit_id.version or 1 |
923 | 85 | if account_id not in account_amount: | 62 | if cv_version > 1: |
924 | 86 | account_amount[account_id] = 0 | 63 | if cv_line.id not in cvl_amount: |
925 | 87 | account_amount[account_id] += round(qty * price_unit, 2) | 64 | cvl_amount[cv_line.id] = 0 |
926 | 88 | 65 | cvl_amount[cv_line.id] += round(qty * price_unit, 2) | |
927 | 89 | if account_amount and po_ids: | 66 | else: |
928 | 90 | inv_obj._update_commitments_lines(cr, uid, po_ids.keys(), account_amount, from_cancel=ids, context=context) | 67 | account_id = inv_obj._get_expense_account(cr, uid, move.purchase_line_id, context=context) |
929 | 68 | if account_id: | ||
930 | 69 | if account_id not in account_amount: | ||
931 | 70 | account_amount[account_id] = 0 | ||
932 | 71 | account_amount[account_id] += round(qty * price_unit, 2) | ||
933 | 72 | if (account_amount or cvl_amount) and po_ids: | ||
934 | 73 | inv_obj._update_commitments_lines(cr, uid, po_ids.keys(), account_amount_dic=account_amount, cvl_amount_dic=cvl_amount, | ||
935 | 74 | from_cancel=ids, context=context) | ||
936 | 91 | 75 | ||
937 | 92 | return super(stock_move, self).action_cancel(cr, uid, ids, context=context) | 76 | return super(stock_move, self).action_cancel(cr, uid, ids, context=context) |
938 | 93 | 77 | ||
939 | 94 | 78 | ||
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 | 55 | create_context = context and dict(context) or {} | 55 | create_context = context and dict(context) or {} |
945 | 56 | if 'res_log_read' in create_context: | 56 | if 'res_log_read' in create_context: |
946 | 57 | vals['read'] = create_context.pop('res_log_read') | 57 | vals['read'] = create_context.pop('res_log_read') |
947 | 58 | if '__copy_data_seen' in create_context: | ||
948 | 59 | create_context.pop('__copy_data_seen') | ||
949 | 58 | if create_context and not vals.get('context'): | 60 | if create_context and not vals.get('context'): |
950 | 59 | vals['context'] = create_context | 61 | vals['context'] = create_context |
951 | 60 | return super(res_log, self).create(cr, uid, vals, context=context) | 62 | return super(res_log, self).create(cr, uid, vals, context=context) |
952 | 61 | 63 | ||
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 | 677 | <field name="method">us_8753_admin_never_expire_password</field> | 677 | <field name="method">us_8753_admin_never_expire_password</field> |
959 | 678 | </record> | 678 | </record> |
960 | 679 | 679 | ||
961 | 680 | <<<<<<< TREE | ||
962 | 680 | <!-- UF22.0 --> | 681 | <!-- UF22.0 --> |
963 | 681 | <record id="us_8805_product_set_archived" model="patch.scripts"> | 682 | <record id="us_8805_product_set_archived" model="patch.scripts"> |
964 | 682 | <field name="method">us_8805_product_set_archived</field> | 683 | <field name="method">us_8805_product_set_archived</field> |
965 | @@ -686,5 +687,11 @@ | |||
966 | 686 | <field name="method">us_8869_remove_ir_import</field> | 687 | <field name="method">us_8869_remove_ir_import</field> |
967 | 687 | </record> | 688 | </record> |
968 | 688 | 689 | ||
969 | 690 | ======= | ||
970 | 691 | <!-- UF22.0 --> | ||
971 | 692 | <record id="us_7449_set_cv_version" model="patch.scripts"> | ||
972 | 693 | <field name="method">us_7449_set_cv_version</field> | ||
973 | 694 | </record> | ||
974 | 695 | >>>>>>> MERGE-SOURCE | ||
975 | 689 | </data> | 696 | </data> |
976 | 690 | </openerp> | 697 | </openerp> |
977 | 691 | 698 | ||
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 | 3925 | #: field:account.invoice.tax,manual:0 | 3925 | #: field:account.invoice.tax,manual:0 |
983 | 3926 | #: view:account.commitment:0 | 3926 | #: view:account.commitment:0 |
984 | 3927 | #: selection:account.commitment,type:0 | 3927 | #: selection:account.commitment,type:0 |
985 | 3928 | #: selection:account.commitment.line,commit_type:0 | ||
986 | 3928 | #: selection:purchase.order,invoice_method:0 | 3929 | #: selection:purchase.order,invoice_method:0 |
987 | 3929 | #: model:ir.ui.menu,name:sync_client.sync_wiz_menu | 3930 | #: model:ir.ui.menu,name:sync_client.sync_wiz_menu |
988 | 3930 | msgid "Manual" | 3931 | msgid "Manual" |
989 | @@ -9634,6 +9635,7 @@ | |||
990 | 9634 | 9635 | ||
991 | 9635 | #. module: analytic_distribution | 9636 | #. module: analytic_distribution |
992 | 9636 | #: selection:account.commitment,type:0 | 9637 | #: selection:account.commitment,type:0 |
993 | 9638 | #: selection:account.commitment.line,commit_type:0 | ||
994 | 9637 | msgid "Manual - ESC supplier" | 9639 | msgid "Manual - ESC supplier" |
995 | 9638 | msgstr "Manuel - Fournisseur ESC" | 9640 | msgstr "Manuel - Fournisseur ESC" |
996 | 9639 | 9641 | ||
997 | @@ -30297,7 +30299,7 @@ | |||
998 | 30297 | #: code:addons/purchase/purchase_order_line.py:1795 | 30299 | #: code:addons/purchase/purchase_order_line.py:1795 |
999 | 30298 | #, python-format | 30300 | #, python-format |
1000 | 30299 | msgid "There is no expense account defined for this product: \"%s\" (id:%d)" | 30301 | msgid "There is no expense account defined for this product: \"%s\" (id:%d)" |
1002 | 30300 | msgstr "Il n'y a pas de compte de charge définit pour ce produit : \"%s\" (id. : %d)" | 30302 | msgstr "Il n'y a pas de compte de charge défini pour ce produit : \"%s\" (id. : %d)" |
1003 | 30301 | 30303 | ||
1004 | 30302 | #. module: msf_outgoing | 30304 | #. module: msf_outgoing |
1005 | 30303 | #: report:packing.list:0 | 30305 | #: report:packing.list:0 |
1006 | @@ -30674,7 +30676,7 @@ | |||
1007 | 30674 | #: code:addons/analytic_distribution_supply/invoice.py:147 | 30676 | #: code:addons/analytic_distribution_supply/invoice.py:147 |
1008 | 30675 | #, python-format | 30677 | #, python-format |
1009 | 30676 | msgid "There is no expense account defined for this PO line: \"%s\" (id:%d)" | 30678 | msgid "There is no expense account defined for this PO line: \"%s\" (id:%d)" |
1011 | 30677 | msgstr "There is no expense account defined for this PO line: \"%s\" (id:%d)" | 30679 | msgstr "Il n'y a pas de compte de charge défini pour cette ligne de BC : \"%s\" (id. : %d)" |
1012 | 30678 | 30680 | ||
1013 | 30679 | #. module: msf_doc_import | 30681 | #. module: msf_doc_import |
1014 | 30680 | #: view:msf.import.export:0 | 30682 | #: view:msf.import.export:0 |
1015 | @@ -44516,12 +44518,6 @@ | |||
1016 | 44516 | msgid "Choose day" | 44518 | msgid "Choose day" |
1017 | 44517 | msgstr "Choisir un jour" | 44519 | msgstr "Choisir un jour" |
1018 | 44518 | 44520 | ||
1019 | 44519 | #. module: analytic_distribution | ||
1020 | 44520 | #: code:addons/analytic_distribution/account_commitment.py:161 | ||
1021 | 44521 | #, python-format | ||
1022 | 44522 | msgid "No analytic distribution found for %s %s" | ||
1023 | 44523 | msgstr "Pas de distribution analytique trouvée pour %s %s" | ||
1024 | 44524 | |||
1025 | 44525 | #. modules: sync_so, analytic_distribution_supply, analytic_override, analytic_distribution | 44521 | #. modules: sync_so, analytic_distribution_supply, analytic_override, analytic_distribution |
1026 | 44526 | #: code:addons/analytic_distribution/wizard/analytic_distribution_wizard.py:322 | 44522 | #: code:addons/analytic_distribution/wizard/analytic_distribution_wizard.py:322 |
1027 | 44527 | #: code:addons/analytic_distribution/wizard/analytic_distribution_wizard.py:352 | 44523 | #: code:addons/analytic_distribution/wizard/analytic_distribution_wizard.py:352 |
1028 | @@ -47492,6 +47488,7 @@ | |||
1029 | 47492 | #: view:account.bank.statement.line:0 | 47488 | #: view:account.bank.statement.line:0 |
1030 | 47493 | #: view:sale.order.line:0 | 47489 | #: view:sale.order.line:0 |
1031 | 47494 | #: view:free.allocation.wizard:0 | 47490 | #: view:free.allocation.wizard:0 |
1032 | 47491 | #: view:account.commitment.line:0 | ||
1033 | 47495 | msgid "Delete" | 47492 | msgid "Delete" |
1034 | 47496 | msgstr "Supprimer" | 47493 | msgstr "Supprimer" |
1035 | 47497 | 47494 | ||
1036 | @@ -68111,7 +68108,7 @@ | |||
1037 | 68111 | #: code:addons/analytic_distribution_supply/invoice.py:227 | 68108 | #: code:addons/analytic_distribution_supply/invoice.py:227 |
1038 | 68112 | #, python-format | 68109 | #, python-format |
1039 | 68113 | msgid "No analytic distribution found." | 68110 | msgid "No analytic distribution found." |
1041 | 68114 | msgstr "Pas de disribution analytique trouvée." | 68111 | msgstr "Pas de distribution analytique trouvée." |
1042 | 68115 | 68112 | ||
1043 | 68116 | #. module: base | 68113 | #. module: base |
1044 | 68117 | #: model:res.currency,currency_name:base.CHF | 68114 | #: model:res.currency,currency_name:base.CHF |
1045 | @@ -69423,9 +69420,32 @@ | |||
1046 | 69423 | 69420 | ||
1047 | 69424 | #. module: analytic_distribution | 69421 | #. module: analytic_distribution |
1048 | 69425 | #: selection:account.commitment,type:0 | 69422 | #: selection:account.commitment,type:0 |
1049 | 69423 | #: selection:account.commitment.line,commit_type:0 | ||
1050 | 69426 | msgid "Automatic - External supplier" | 69424 | msgid "Automatic - External supplier" |
1051 | 69427 | msgstr "Automatique - Fournisseur Externe" | 69425 | msgstr "Automatique - Fournisseur Externe" |
1052 | 69428 | 69426 | ||
1053 | 69427 | #. module: analytic_distribution | ||
1054 | 69428 | #: selection:account.commitment,type:0 | ||
1055 | 69429 | #: selection:account.commitment.line,commit_type:0 | ||
1056 | 69430 | msgid "Automatic - Intermission" | ||
1057 | 69431 | msgstr "Automatique - Intermission" | ||
1058 | 69432 | |||
1059 | 69433 | #. module: analytic_distribution | ||
1060 | 69434 | #: selection:account.commitment,type:0 | ||
1061 | 69435 | #: selection:account.commitment.line,commit_type:0 | ||
1062 | 69436 | msgid "Automatic - Intersection" | ||
1063 | 69437 | msgstr "Automatique - Intersection" | ||
1064 | 69438 | |||
1065 | 69439 | #. module: analytic_distribution | ||
1066 | 69440 | #: view:account.commitment:0 | ||
1067 | 69441 | msgid "Intermission Commitment Voucher" | ||
1068 | 69442 | msgstr "Bon d'Engagement Intermission" | ||
1069 | 69443 | |||
1070 | 69444 | #. module: analytic_distribution | ||
1071 | 69445 | #: view:account.commitment:0 | ||
1072 | 69446 | msgid "Intersection Commitment Voucher" | ||
1073 | 69447 | msgstr "Bon d'Engagement Intersection" | ||
1074 | 69448 | |||
1075 | 69429 | #. module: procurement | 69449 | #. module: procurement |
1076 | 69430 | #: field:procurement.order,close_move:0 | 69450 | #: field:procurement.order,close_move:0 |
1077 | 69431 | msgid "Close Move at end" | 69451 | msgid "Close Move at end" |
1078 | @@ -82184,7 +82204,7 @@ | |||
1079 | 82184 | msgid "SFTP connection succeeded" | 82204 | msgid "SFTP connection succeeded" |
1080 | 82185 | msgstr "SFTP connection succeeded" | 82205 | msgstr "SFTP connection succeeded" |
1081 | 82186 | 82206 | ||
1083 | 82187 | #. 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 | 82207 | #. 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 | 82188 | #: field:account.analytic.line,product_id:0 | 82208 | #: field:account.analytic.line,product_id:0 |
1085 | 82189 | #: view:account.entries.report:0 | 82209 | #: view:account.entries.report:0 |
1086 | 82190 | #: field:account.entries.report,product_id:0 | 82210 | #: field:account.entries.report,product_id:0 |
1087 | @@ -82390,6 +82410,7 @@ | |||
1088 | 82390 | #: view:replenishment.segment.line.amc.past_fmc:0 | 82410 | #: view:replenishment.segment.line.amc.past_fmc:0 |
1089 | 82391 | #: view:replenishment.segment.line.min_max_auto_supply.history:0 | 82411 | #: view:replenishment.segment.line.min_max_auto_supply.history:0 |
1090 | 82392 | #: field:view.expired.expiring.stock.lines,product_id:0 | 82412 | #: field:view.expired.expiring.stock.lines,product_id:0 |
1091 | 82413 | #: field:account.commitment.line,po_line_product_id:0 | ||
1092 | 82393 | #, python-format | 82414 | #, python-format |
1093 | 82394 | msgid "Product" | 82415 | msgid "Product" |
1094 | 82395 | msgstr "Produit" | 82416 | msgstr "Produit" |
1095 | @@ -87510,7 +87531,7 @@ | |||
1096 | 87510 | msgstr "Ceci est utilisé uniquement si vous sélectionnez une localisation de type chainée.\n" | 87531 | msgstr "Ceci est utilisé uniquement si vous sélectionnez une localisation de type chainée.\n" |
1097 | 87511 | "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." | 87532 | "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 | 87512 | 87533 | ||
1100 | 87513 | #. modules: msf_budget, sync_client, update_client, kit, base, msf_profile | 87534 | #. modules: msf_budget, sync_client, update_client, kit, base, msf_profile, analytic_distribution |
1101 | 87514 | #: view:ir.module.module:0 | 87535 | #: view:ir.module.module:0 |
1102 | 87515 | #: report:ir.module.reference:0 | 87536 | #: report:ir.module.reference:0 |
1103 | 87516 | #: view:composition.kit:0 | 87537 | #: view:composition.kit:0 |
1104 | @@ -87528,6 +87549,7 @@ | |||
1105 | 87528 | #: field:sync.client.update_to_send,version:0 | 87549 | #: field:sync.client.update_to_send,version:0 |
1106 | 87529 | #: field:sync.version.instance.monitor,version:0 | 87550 | #: field:sync.version.instance.monitor,version:0 |
1107 | 87530 | #: field:sync_client.version,name:0 | 87551 | #: field:sync_client.version,name:0 |
1108 | 87552 | #: field:account.commitment,version:0 | ||
1109 | 87531 | msgid "Version" | 87553 | msgid "Version" |
1110 | 87532 | msgstr "Version" | 87554 | msgstr "Version" |
1111 | 87533 | 87555 | ||
1112 | @@ -91254,13 +91276,21 @@ | |||
1113 | 91254 | msgid "Purchase Orders Waiting Confirmation" | 91276 | msgid "Purchase Orders Waiting Confirmation" |
1114 | 91255 | msgstr "Bons de Commandes en attente de Confirmation" | 91277 | msgstr "Bons de Commandes en attente de Confirmation" |
1115 | 91256 | 91278 | ||
1117 | 91257 | #. modules: purchase, analytic_distribution, purchase_override | 91279 | #. modules: purchase, analytic_distribution, purchase_override, account_override, register_accounting |
1118 | 91258 | #: field:account.commitment,line_ids:0 | 91280 | #: field:account.commitment,line_ids:0 |
1119 | 91259 | #: view:account.commitment.line:0 | 91281 | #: view:account.commitment.line:0 |
1120 | 91282 | #: field:purchase.order.line,cv_line_ids:0 | ||
1121 | 91283 | #: field:purchase.order.merged.line,cv_line_ids:0 | ||
1122 | 91284 | #: field:account.invoice.line,cv_line_ids:0 | ||
1123 | 91285 | #: field:wizard.account.invoice.line,cv_line_ids:0 | ||
1124 | 91286 | msgid "Commitment Voucher Lines" | ||
1125 | 91287 | msgstr "Lignes de Bon d'Engagement" | ||
1126 | 91288 | |||
1127 | 91289 | #. modules: purchase, purchase_override | ||
1128 | 91260 | #: field:purchase.order.line,commitment_line_ids:0 | 91290 | #: field:purchase.order.line,commitment_line_ids:0 |
1129 | 91261 | #: field:purchase.order.merged.line,commitment_line_ids:0 | 91291 | #: field:purchase.order.merged.line,commitment_line_ids:0 |
1132 | 91262 | msgid "Commitment Voucher Lines" | 91292 | msgid "Commitment Voucher Lines (deprecated)" |
1133 | 91263 | msgstr "Lignes de Bon d'Engagement" | 91293 | msgstr "Lignes de Bon d'Engagement (obsolète)" |
1134 | 91264 | 91294 | ||
1135 | 91265 | #. module: sync_client | 91295 | #. module: sync_client |
1136 | 91266 | #: code:addons/sync_client/hq_monitor.py:48 | 91296 | #: code:addons/sync_client/hq_monitor.py:48 |
1137 | @@ -98351,13 +98381,17 @@ | |||
1138 | 98351 | msgstr "Type de note" | 98381 | msgstr "Type de note" |
1139 | 98352 | 98382 | ||
1140 | 98353 | #. modules: purchase, analytic_distribution, msf_doc_import, finance | 98383 | #. modules: purchase, analytic_distribution, msf_doc_import, finance |
1141 | 98354 | #: field:account.commitment.line,purchase_order_line_ids:0 | ||
1142 | 98355 | #: view:purchase.order.line:0 | 98384 | #: view:purchase.order.line:0 |
1143 | 98356 | #: field:wizard.import.po,line_ids:0 | 98385 | #: field:wizard.import.po,line_ids:0 |
1144 | 98357 | #: field:account.invoice.line,purchase_order_line_ids:0 | 98386 | #: field:account.invoice.line,purchase_order_line_ids:0 |
1145 | 98358 | msgid "Purchase Order Lines" | 98387 | msgid "Purchase Order Lines" |
1146 | 98359 | msgstr "Lignes Bon de Commande" | 98388 | msgstr "Lignes Bon de Commande" |
1147 | 98360 | 98389 | ||
1148 | 98390 | #. module: analytic_distribution | ||
1149 | 98391 | #: field:account.commitment.line,purchase_order_line_ids:0 | ||
1150 | 98392 | msgid "Purchase Order Lines (deprecated)" | ||
1151 | 98393 | msgstr "Lignes Bon de Commande (obsolète)" | ||
1152 | 98394 | |||
1153 | 98361 | #. module: specific_rules | 98395 | #. module: specific_rules |
1154 | 98362 | #: field:unconsistent.stock.report.line,product_bn:0 | 98396 | #: field:unconsistent.stock.report.line,product_bn:0 |
1155 | 98363 | msgid "BN management" | 98397 | msgid "BN management" |
1156 | @@ -112379,6 +112413,7 @@ | |||
1157 | 112379 | #, python-format | 112413 | #, python-format |
1158 | 112380 | msgid "The Pre-Packing List is using a deactivated Delivery Address (%s). Please select another one to be able to process." | 112414 | msgid "The Pre-Packing List is using a deactivated Delivery Address (%s). Please select another one to be able to process." |
1159 | 112381 | 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." | 112415 | 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 | 112416 | <<<<<<< TREE | ||
1161 | 112382 | 112417 | ||
1162 | 112383 | #. module: sync_so | 112418 | #. module: sync_so |
1163 | 112384 | #: code:addons/sync_so/so_po_common.py:491 | 112419 | #: code:addons/sync_so/so_po_common.py:491 |
1164 | @@ -112719,3 +112754,47 @@ | |||
1165 | 112719 | #, python-format | 112754 | #, python-format |
1166 | 112720 | msgid "Products moved in the last: %s month%s" | 112755 | msgid "Products moved in the last: %s month%s" |
1167 | 112721 | msgstr "Mouvements de stock dans: %s dernier%s mois" | 112756 | msgstr "Mouvements de stock dans: %s dernier%s mois" |
1168 | 112757 | ======= | ||
1169 | 112758 | |||
1170 | 112759 | #. module: sync_so | ||
1171 | 112760 | #: code:addons/sync_so/so_po_common.py:491 | ||
1172 | 112761 | #, python-format | ||
1173 | 112762 | msgid "Cannot process Document/line due to Product Code %s which does not exist in this instance" | ||
1174 | 112763 | msgstr "Impossible de traiter le Document/la ligne. Le Code Produit %s n'existe pas dans cette instance" | ||
1175 | 112764 | |||
1176 | 112765 | #. module: analytic_distribution | ||
1177 | 112766 | #: help:account.commitment,version:0 | ||
1178 | 112767 | msgid "Technical field to distinguish old CVs from new ones which have a different behavior." | ||
1179 | 112768 | msgstr "Champ technique pour distinguer les anciens Bons d'Engagement des nouveaux qui ont un comportement différent." | ||
1180 | 112769 | |||
1181 | 112770 | #. module: analytic_distribution | ||
1182 | 112771 | #: field:account.commitment,display_super_done_button:0 | ||
1183 | 112772 | msgid "Display the button allowing to always set a CV to Done" | ||
1184 | 112773 | msgstr "Afficher le bouton permettant de toujours passer un Bon d'Engagement en Terminé" | ||
1185 | 112774 | |||
1186 | 112775 | #. module: analytic_distribution | ||
1187 | 112776 | #: field:account.commitment.line,commit_type:0 | ||
1188 | 112777 | msgid "Commitment Voucher Type" | ||
1189 | 112778 | msgstr "Type de Bon d'Engagement" | ||
1190 | 112779 | |||
1191 | 112780 | #. module: analytic_distribution | ||
1192 | 112781 | #: field:account.commitment.line,po_line_id:0 | ||
1193 | 112782 | #: field:account.commitment.line,po_line_number:0 | ||
1194 | 112783 | msgid "PO Line" | ||
1195 | 112784 | msgstr "Ligne du BC" | ||
1196 | 112785 | |||
1197 | 112786 | #. module: analytic_distribution | ||
1198 | 112787 | #: view:account.commitment:0 | ||
1199 | 112788 | msgid "Done (for Administrator only)" | ||
1200 | 112789 | msgstr "Terminé (pour l'Administrateur uniquement)" | ||
1201 | 112790 | |||
1202 | 112791 | #. module: analytic_distribution | ||
1203 | 112792 | #: view:account.commitment:0 | ||
1204 | 112793 | msgid "You are about to set this Commitment Voucher to the state \"Done\". Do you want to proceed?" | ||
1205 | 112794 | msgstr "Vous êtes sur le point de passer ce Bon d'Engagement à l'état \"Terminé\". Voulez-vous continuer ?" | ||
1206 | 112795 | |||
1207 | 112796 | #. module: analytic_distribution | ||
1208 | 112797 | #: view:account.commitment.line:0 | ||
1209 | 112798 | msgid "Do you really want to delete this line?" | ||
1210 | 112799 | msgstr "Voulez-vous vraiment supprimer cette ligne ?" | ||
1211 | 112800 | >>>>>>> MERGE-SOURCE | ||
1212 | 112722 | 112801 | ||
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 | 53 | 'model': lambda *a: 'patch.scripts', | 53 | 'model': lambda *a: 'patch.scripts', |
1218 | 54 | } | 54 | } |
1219 | 55 | 55 | ||
1220 | 56 | <<<<<<< TREE | ||
1221 | 56 | # UF22.0 | 57 | # UF22.0 |
1222 | 57 | def us_8805_product_set_archived(self, cr, uid, *a, **b): | 58 | def us_8805_product_set_archived(self, cr, uid, *a, **b): |
1223 | 58 | if self.pool.get('sync_client.version') and self.pool.get('sync.client.entity'): | 59 | if self.pool.get('sync_client.version') and self.pool.get('sync.client.entity'): |
1224 | @@ -80,6 +81,18 @@ | |||
1225 | 80 | cr.execute("update internal_request_import set file_to_import=NULL"); | 81 | cr.execute("update internal_request_import set file_to_import=NULL"); |
1226 | 81 | return True | 82 | return True |
1227 | 82 | 83 | ||
1228 | 84 | ======= | ||
1229 | 85 | # UF22.0 | ||
1230 | 86 | def us_7449_set_cv_version(self, cr, uid, *a, **b): | ||
1231 | 87 | """ | ||
1232 | 88 | Sets the existing Commitment Vouchers in version 1. | ||
1233 | 89 | """ | ||
1234 | 90 | if self.pool.get('sync.client.entity'): # existing instances | ||
1235 | 91 | cr.execute("UPDATE account_commitment SET version = 1") | ||
1236 | 92 | self._logger.warn('Commitment Vouchers: %s CV(s) set to version 1.', cr.rowcount) | ||
1237 | 93 | return True | ||
1238 | 94 | |||
1239 | 95 | >>>>>>> MERGE-SOURCE | ||
1240 | 83 | # UF21.1 | 96 | # UF21.1 |
1241 | 84 | def us_8810_fake_updates(self, cr, uid, *a, **b): | 97 | def us_8810_fake_updates(self, cr, uid, *a, **b): |
1242 | 85 | if self.pool.get('sync.client.entity'): | 98 | if self.pool.get('sync.client.entity'): |
1243 | 86 | 99 | ||
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 | 2887 | ('instance_id', '=', self.pool.get('res.users').browse(cr, uid, uid, context).company_id.instance_id.id) | 2887 | ('instance_id', '=', self.pool.get('res.users').browse(cr, uid, uid, context).company_id.instance_id.id) |
1249 | 2888 | ], limit=1, context=context) | 2888 | ], limit=1, context=context) |
1250 | 2889 | 2889 | ||
1251 | 2890 | po_partner_type = po.partner_id.partner_type | ||
1252 | 2891 | if po_partner_type == 'external': | ||
1253 | 2892 | cv_type = 'external' | ||
1254 | 2893 | elif po_partner_type == 'section': | ||
1255 | 2894 | cv_type = 'intersection' | ||
1256 | 2895 | elif po_partner_type == 'intermission': | ||
1257 | 2896 | cv_type = 'intermission' | ||
1258 | 2897 | else: | ||
1259 | 2898 | cv_type = 'manual' | ||
1260 | 2890 | vals = { | 2899 | vals = { |
1261 | 2891 | 'journal_id': engagement_ids and engagement_ids[0] or False, | 2900 | 'journal_id': engagement_ids and engagement_ids[0] or False, |
1262 | 2892 | 'currency_id': po.currency_id and po.currency_id.id or False, | 2901 | 'currency_id': po.currency_id and po.currency_id.id or False, |
1263 | 2893 | 'partner_id': po.partner_id and po.partner_id.id or False, | 2902 | 'partner_id': po.partner_id and po.partner_id.id or False, |
1264 | 2894 | 'purchase_id': po.id or False, | 2903 | 'purchase_id': po.id or False, |
1266 | 2895 | 'type': 'external' if po.partner_id.partner_type == 'external' else 'manual', | 2904 | 'type': cv_type, |
1267 | 2896 | } | 2905 | } |
1268 | 2897 | # prepare some values | 2906 | # prepare some values |
1269 | 2898 | period_ids = get_period_from_date(self, cr, uid, cv_date, context=context) | 2907 | period_ids = get_period_from_date(self, cr, uid, cv_date, context=context) |
1270 | 2899 | 2908 | ||
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 | 568 | # finance | 568 | # finance |
1276 | 569 | 'analytic_distribution_id': fields.many2one('analytic.distribution', 'Analytic Distribution'), | 569 | 'analytic_distribution_id': fields.many2one('analytic.distribution', 'Analytic Distribution'), |
1277 | 570 | 'have_analytic_distribution_from_header': fields.function(_have_analytic_distribution_from_header, method=True, type='boolean', string='Header Distrib.?'), | 570 | 'have_analytic_distribution_from_header': fields.function(_have_analytic_distribution_from_header, method=True, type='boolean', string='Header Distrib.?'), |
1278 | 571 | # for CV in version 1 | ||
1279 | 571 | 'commitment_line_ids': fields.many2many('account.commitment.line', 'purchase_line_commitment_rel', 'purchase_id', 'commitment_id', | 572 | 'commitment_line_ids': fields.many2many('account.commitment.line', 'purchase_line_commitment_rel', 'purchase_id', 'commitment_id', |
1281 | 572 | string="Commitment Voucher Lines", readonly=True), | 573 | string="Commitment Voucher Lines (deprecated)", readonly=True), |
1282 | 574 | # for CV starting from version 2 | ||
1283 | 575 | # note: cv_line_ids is a o2m because of the related m2o on CV lines but it should only contain one CV line | ||
1284 | 576 | 'cv_line_ids': fields.one2many('account.commitment.line', 'po_line_id', string="Commitment Voucher Lines"), | ||
1285 | 573 | 'analytic_distribution_state': fields.function(_get_distribution_state, method=True, type='selection', | 577 | 'analytic_distribution_state': fields.function(_get_distribution_state, method=True, type='selection', |
1286 | 574 | selection=[('none', 'None'), ('valid', 'Valid'), ('invalid', 'Invalid')], | 578 | selection=[('none', 'None'), ('valid', 'Valid'), ('invalid', 'Invalid')], |
1287 | 575 | string="Distribution state", help="Informs from distribution state among 'none', 'valid', 'invalid."), | 579 | string="Distribution state", help="Informs from distribution state among 'none', 'valid', 'invalid."), |
1288 | @@ -1295,7 +1299,7 @@ | |||
1289 | 1295 | self.pool.get('product.product')._get_restriction_error(cr, uid, [pol.product_id.id], | 1299 | self.pool.get('product.product')._get_restriction_error(cr, uid, [pol.product_id.id], |
1290 | 1296 | {'partner_id': pol.order_id.partner_id.id}, context=context) | 1300 | {'partner_id': pol.order_id.partner_id.id}, context=context) |
1291 | 1297 | 1301 | ||
1293 | 1298 | default.update({'state': 'draft', 'move_ids': [], 'invoiced': 0, 'invoice_lines': [], 'commitment_line_ids': [], }) | 1302 | default.update({'state': 'draft', 'move_ids': [], 'invoiced': 0, 'invoice_lines': [], 'commitment_line_ids': [], 'cv_line_ids': [], }) |
1294 | 1299 | 1303 | ||
1295 | 1300 | 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']: | 1304 | 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 | 1301 | if field not in default: | 1305 | if field not in default: |
1297 | @@ -1868,14 +1872,17 @@ | |||
1298 | 1868 | 1872 | ||
1299 | 1869 | import_commitments = self.pool.get('unifield.setup.configuration').get_config(cr, uid).import_commitments | 1873 | import_commitments = self.pool.get('unifield.setup.configuration').get_config(cr, uid).import_commitments |
1300 | 1870 | for pol in self.browse(cr, uid, ids, context=context): | 1874 | for pol in self.browse(cr, uid, ids, context=context): |
1303 | 1871 | # only create CV for external and ESC partners: | 1875 | if pol.order_id.partner_id.partner_type == 'internal': |
1302 | 1872 | if pol.order_id.partner_id.partner_type not in ['external', 'esc']: | ||
1304 | 1873 | return False | 1876 | return False |
1305 | 1874 | 1877 | ||
1306 | 1875 | if pol.order_id.partner_id.partner_type == 'esc' and import_commitments: | 1878 | if pol.order_id.partner_id.partner_type == 'esc' and import_commitments: |
1307 | 1876 | return False | 1879 | return False |
1308 | 1877 | 1880 | ||
1310 | 1878 | if pol.order_id.order_type in ['loan', 'in_kind']: | 1881 | if pol.order_id.order_type in ['loan', 'in_kind', 'donation_st', 'donation_exp']: |
1311 | 1882 | return False | ||
1312 | 1883 | |||
1313 | 1884 | # exclude push flow (FO or FO line created first) | ||
1314 | 1885 | if pol.order_id.push_fo or pol.set_as_sourced_n: | ||
1315 | 1879 | return False | 1886 | return False |
1316 | 1880 | 1887 | ||
1317 | 1881 | commitment_voucher_id = self.pool.get('account.commitment').search(cr, uid, [('purchase_id', '=', pol.order_id.id), ('state', '=', 'draft')], context=context) | 1888 | 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 | 1886 | raise osv.except_osv(_('Error'), _('Delivery Confirmed Date is a mandatory field.')) | 1893 | raise osv.except_osv(_('Error'), _('Delivery Confirmed Date is a mandatory field.')) |
1320 | 1887 | 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) | 1894 | 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 | 1888 | 1895 | ||
1322 | 1889 | # group PO line by account_id: | ||
1323 | 1890 | expense_account = pol.account_4_distribution and pol.account_4_distribution.id or False | 1896 | expense_account = pol.account_4_distribution and pol.account_4_distribution.id or False |
1324 | 1891 | if not expense_account: | 1897 | if not expense_account: |
1325 | 1892 | raise osv.except_osv(_('Error'), _('There is no expense account defined for this line: %s (id:%d)') % (pol.name or '', pol.id)) | 1898 | raise osv.except_osv(_('Error'), _('There is no expense account defined for this line: %s (id:%d)') % (pol.name or '', pol.id)) |
1326 | 1893 | 1899 | ||
1327 | 1900 | # in CV in version 1, PO lines are grouped by account_id. Else 1 PO line generates 1 CV line. | ||
1328 | 1901 | cv_version = self.pool.get('account.commitment').read(cr, uid, commitment_voucher_id, ['version'], context=context)['version'] | ||
1329 | 1902 | cc_lines = [] | ||
1330 | 1903 | ad_header = [] # if filled in, the line itself has no AD but uses the one at header level | ||
1331 | 1894 | if pol.analytic_distribution_id: | 1904 | if pol.analytic_distribution_id: |
1332 | 1895 | cc_lines = pol.analytic_distribution_id.cost_center_lines | 1905 | cc_lines = pol.analytic_distribution_id.cost_center_lines |
1334 | 1896 | else: | 1906 | elif cv_version < 2: |
1335 | 1907 | # 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 | 1897 | cc_lines = pol.order_id.analytic_distribution_id.cost_center_lines | 1908 | cc_lines = pol.order_id.analytic_distribution_id.cost_center_lines |
1337 | 1909 | else: | ||
1338 | 1910 | ad_header = pol.order_id.analytic_distribution_id.cost_center_lines | ||
1339 | 1898 | 1911 | ||
1341 | 1899 | if not cc_lines: | 1912 | if not cc_lines and not ad_header: |
1342 | 1900 | raise osv.except_osv(_('Warning'), _('Analytic allocation is mandatory for %s on the line %s for the product %s! It must be added manually.') | 1913 | 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 | 1901 | % (pol.order_id.name, pol.line_number, pol.product_id and pol.product_id.default_code or pol.name or '')) | 1914 | % (pol.order_id.name, pol.line_number, pol.product_id and pol.product_id.default_code or pol.name or '')) |
1344 | 1902 | 1915 | ||
1350 | 1903 | 1916 | new_cv_line = False | |
1351 | 1904 | commit_line_id = self.pool.get('account.commitment.line').search(cr, uid, [('commit_id', '=', commitment_voucher_id), ('account_id', '=', expense_account)], context=context) | 1917 | if cv_version > 1: |
1352 | 1905 | if not commit_line_id: # create new commitment line | 1918 | new_cv_line = True |
1353 | 1906 | distrib_id = self.pool.get('analytic.distribution').create(cr, uid, {}, context=context) | 1919 | else: |
1354 | 1907 | commit_line_id = self.pool.get('account.commitment.line').create(cr, uid, { | 1920 | commit_line_id = self.pool.get('account.commitment.line').search(cr, uid, |
1355 | 1921 | [('commit_id', '=', commitment_voucher_id), | ||
1356 | 1922 | ('account_id', '=', expense_account)], context=context) | ||
1357 | 1923 | if not commit_line_id: | ||
1358 | 1924 | new_cv_line = True | ||
1359 | 1925 | if new_cv_line: # create new commitment line | ||
1360 | 1926 | if ad_header: # the line has no AD itself, it uses the AD at header level | ||
1361 | 1927 | distrib_id = False | ||
1362 | 1928 | else: | ||
1363 | 1929 | distrib_id = self.pool.get('analytic.distribution').create(cr, uid, {}, context=context) | ||
1364 | 1930 | commit_line_vals = { | ||
1365 | 1908 | 'commit_id': commitment_voucher_id, | 1931 | 'commit_id': commitment_voucher_id, |
1366 | 1909 | 'account_id': expense_account, | 1932 | 'account_id': expense_account, |
1367 | 1910 | 'amount': pol.price_subtotal, | 1933 | 'amount': pol.price_subtotal, |
1368 | 1911 | 'initial_amount': pol.price_subtotal, | 1934 | 'initial_amount': pol.price_subtotal, |
1369 | 1912 | 'purchase_order_line_ids': [(4, pol.id)], | ||
1370 | 1913 | 'analytic_distribution_id': distrib_id, | 1935 | 'analytic_distribution_id': distrib_id, |
1382 | 1914 | }, context=context) | 1936 | } |
1383 | 1915 | for aline in cc_lines: | 1937 | if cv_version > 1: |
1384 | 1916 | vals = { | 1938 | commit_line_vals.update({'po_line_id': pol.id, }) |
1385 | 1917 | 'distribution_id': distrib_id, | 1939 | else: |
1386 | 1918 | 'analytic_id': aline.analytic_id.id, | 1940 | commit_line_vals.update({'purchase_order_line_ids': [(4, pol.id)], }) |
1387 | 1919 | 'currency_id': pol.order_id.currency_id.id, | 1941 | commit_line_id = self.pool.get('account.commitment.line').create(cr, uid, commit_line_vals, context=context) |
1388 | 1920 | 'destination_id': aline.destination_id.id, | 1942 | if distrib_id: |
1389 | 1921 | 'percentage': aline.percentage, | 1943 | for aline in cc_lines: |
1390 | 1922 | } | 1944 | vals = { |
1391 | 1923 | self.pool.get('cost.center.distribution.line').create(cr, uid, vals, context=context) | 1945 | 'distribution_id': distrib_id, |
1392 | 1924 | self.pool.get('analytic.distribution').create_funding_pool_lines(cr, uid, [distrib_id], expense_account, context=context) | 1946 | 'analytic_id': aline.analytic_id.id, |
1393 | 1947 | 'currency_id': pol.order_id.currency_id.id, | ||
1394 | 1948 | 'destination_id': aline.destination_id.id, | ||
1395 | 1949 | 'percentage': aline.percentage, | ||
1396 | 1950 | } | ||
1397 | 1951 | self.pool.get('cost.center.distribution.line').create(cr, uid, vals, context=context) | ||
1398 | 1952 | self.pool.get('analytic.distribution').create_funding_pool_lines(cr, uid, [distrib_id], expense_account, context=context) | ||
1399 | 1925 | 1953 | ||
1400 | 1926 | else: # update existing commitment line: | 1954 | else: # update existing commitment line: |
1401 | 1927 | commit_line_id = commit_line_id[0] | 1955 | commit_line_id = commit_line_id[0] |
1402 | 1928 | 1956 | ||
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 | 34 | 'purchase_id': False, | 34 | 'purchase_id': False, |
1408 | 35 | } | 35 | } |
1409 | 36 | 36 | ||
1410 | 37 | def _get_address_invoice(self, cr, uid, picking): | ||
1411 | 38 | """ Gets invoice address of a partner | ||
1412 | 39 | @return {'contact': address, 'invoice': address} for invoice | ||
1413 | 40 | """ | ||
1414 | 41 | res = super(stock_picking, self)._get_address_invoice(cr, uid, picking) | ||
1415 | 42 | if picking.purchase_id: | ||
1416 | 43 | partner_obj = self.pool.get('res.partner') | ||
1417 | 44 | partner = picking.purchase_id.partner_id or picking.address_id.partner_id | ||
1418 | 45 | data = partner_obj.address_get(cr, uid, [partner.id], | ||
1419 | 46 | ['contact', 'invoice']) | ||
1420 | 47 | res.update(data) | ||
1421 | 48 | return res | ||
1422 | 49 | |||
1423 | 50 | def get_currency_id(self, cursor, user, picking): | ||
1424 | 51 | if picking.purchase_id: | ||
1425 | 52 | return picking.purchase_id.pricelist_id.currency_id.id | ||
1426 | 53 | else: | ||
1427 | 54 | return super(stock_picking, self).get_currency_id(cursor, user, picking) | ||
1428 | 55 | |||
1429 | 56 | def _get_comment_invoice(self, cursor, user, picking): | ||
1430 | 57 | if picking.purchase_id and picking.purchase_id.notes: | ||
1431 | 58 | if picking.note: | ||
1432 | 59 | return picking.note + '\n' + picking.purchase_id.notes | ||
1433 | 60 | else: | ||
1434 | 61 | return picking.purchase_id.notes | ||
1435 | 62 | return super(stock_picking, self)._get_comment_invoice(cursor, user, picking) | ||
1436 | 63 | |||
1437 | 64 | def _get_price_unit_invoice(self, cursor, user, move_line, type): | ||
1438 | 65 | if move_line.purchase_line_id: | ||
1439 | 66 | return move_line.purchase_line_id.price_unit | ||
1440 | 67 | return super(stock_picking, self)._get_price_unit_invoice(cursor, user, move_line, type) | ||
1441 | 68 | |||
1442 | 69 | def _get_discount_invoice(self, cursor, user, move_line): | ||
1443 | 70 | if move_line.purchase_line_id: | ||
1444 | 71 | return 0.0 | ||
1445 | 72 | return super(stock_picking, self)._get_discount_invoice(cursor, user, move_line) | ||
1446 | 73 | |||
1447 | 74 | def _get_taxes_invoice(self, cursor, user, move_line, type): | ||
1448 | 75 | if move_line.purchase_line_id: | ||
1449 | 76 | return [x.id for x in move_line.purchase_line_id.taxes_id] | ||
1450 | 77 | return super(stock_picking, self)._get_taxes_invoice(cursor, user, move_line, type) | ||
1451 | 78 | |||
1452 | 79 | def _get_account_analytic_invoice(self, cursor, user, picking, move_line): | ||
1453 | 80 | if move_line.purchase_line_id: | ||
1454 | 81 | return move_line.purchase_line_id.account_analytic_id.id | ||
1455 | 82 | return super(stock_picking, self)._get_account_analytic_invoice(cursor, user, picking, move_line) | ||
1456 | 83 | |||
1457 | 84 | def _invoice_hook(self, cursor, user, picking, invoice_id): | ||
1458 | 85 | purchase_obj = self.pool.get('purchase.order') | ||
1459 | 86 | if picking.purchase_id: | ||
1460 | 87 | purchase_obj.write(cursor, user, [picking.purchase_id.id], {'invoice_id': invoice_id,}) | ||
1461 | 88 | return super(stock_picking, self)._invoice_hook(cursor, user, picking, invoice_id) | ||
1462 | 89 | 37 | ||
1463 | 90 | stock_picking() | 38 | stock_picking() |
1464 | 91 | 39 | ||
1465 | 92 | 40 | ||
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 | 40 | 'sale_id': False | 40 | 'sale_id': False |
1471 | 41 | } | 41 | } |
1472 | 42 | 42 | ||
1473 | 43 | def get_currency_id(self, cursor, user, picking): | ||
1474 | 44 | if picking.sale_id: | ||
1475 | 45 | return picking.sale_id.pricelist_id.currency_id.id | ||
1476 | 46 | else: | ||
1477 | 47 | return super(stock_picking, self).get_currency_id(cursor, user, picking) | ||
1478 | 48 | |||
1479 | 49 | def _get_payment_term(self, cursor, user, picking): | ||
1480 | 50 | if picking.sale_id and picking.sale_id.payment_term: | ||
1481 | 51 | return picking.sale_id.payment_term.id | ||
1482 | 52 | return super(stock_picking, self)._get_payment_term(cursor, user, picking) | ||
1483 | 53 | |||
1484 | 54 | def _get_address_invoice(self, cursor, user, picking): | ||
1485 | 55 | res = {} | ||
1486 | 56 | if picking.sale_id: | ||
1487 | 57 | res['contact'] = picking.sale_id.partner_order_id.id | ||
1488 | 58 | res['invoice'] = picking.sale_id.partner_invoice_id.id | ||
1489 | 59 | return res | ||
1490 | 60 | return super(stock_picking, self)._get_address_invoice(cursor, user, picking) | ||
1491 | 61 | |||
1492 | 62 | def _get_comment_invoice(self, cursor, user, picking): | ||
1493 | 63 | if picking.note or (picking.sale_id and picking.sale_id.note): | ||
1494 | 64 | return picking.note or picking.sale_id.note | ||
1495 | 65 | return super(stock_picking, self)._get_comment_invoice(cursor, user, picking) | ||
1496 | 66 | |||
1497 | 67 | def _get_price_unit_invoice(self, cursor, user, move_line, type): | ||
1498 | 68 | if move_line.sale_line_id and move_line.sale_line_id.product_id.id == move_line.product_id.id: | ||
1499 | 69 | uom_id = move_line.product_id.uom_id.id | ||
1500 | 70 | uos_id = move_line.product_id.uos_id and move_line.product_id.uos_id.id or False | ||
1501 | 71 | price = move_line.sale_line_id.price_unit | ||
1502 | 72 | coeff = move_line.product_id.uos_coeff | ||
1503 | 73 | if uom_id != uos_id and coeff != 0: | ||
1504 | 74 | price_unit = price / coeff | ||
1505 | 75 | return price_unit | ||
1506 | 76 | return move_line.sale_line_id.price_unit | ||
1507 | 77 | return super(stock_picking, self)._get_price_unit_invoice(cursor, user, move_line, type) | ||
1508 | 78 | |||
1509 | 79 | def _get_discount_invoice(self, cursor, user, move_line): | ||
1510 | 80 | if move_line.sale_line_id: | ||
1511 | 81 | return move_line.sale_line_id.discount | ||
1512 | 82 | return super(stock_picking, self)._get_discount_invoice(cursor, user, move_line) | ||
1513 | 83 | |||
1514 | 84 | def _get_taxes_invoice(self, cursor, user, move_line, type): | ||
1515 | 85 | if move_line.sale_line_id and move_line.sale_line_id.product_id.id == move_line.product_id.id: | ||
1516 | 86 | return [x.id for x in move_line.sale_line_id.tax_id] | ||
1517 | 87 | return super(stock_picking, self)._get_taxes_invoice(cursor, user, move_line, type) | ||
1518 | 88 | |||
1519 | 89 | def _get_account_analytic_invoice(self, cursor, user, picking, move_line): | ||
1520 | 90 | if picking.sale_id: | ||
1521 | 91 | return picking.sale_id.project_id.id | ||
1522 | 92 | return super(stock_picking, self)._get_account_analytic_invoice(cursor, user, picking, move_line) | ||
1523 | 93 | |||
1524 | 94 | def _invoice_hook(self, cursor, user, picking, invoice_id): | ||
1525 | 95 | sale_obj = self.pool.get('sale.order') | ||
1526 | 96 | if picking.sale_id: | ||
1527 | 97 | sale_obj.write(cursor, user, [picking.sale_id.id], { | ||
1528 | 98 | 'invoice_ids': [(4, invoice_id)], | ||
1529 | 99 | }) | ||
1530 | 100 | return super(stock_picking, self)._invoice_hook(cursor, user, picking, invoice_id) | ||
1531 | 101 | |||
1532 | 102 | 43 | ||
1533 | 103 | stock_picking() | 44 | stock_picking() |
1534 | 104 | 45 | ||
1535 | 105 | 46 | ||
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 | 1193 | return True | 1193 | return True |
1541 | 1194 | 1194 | ||
1542 | 1195 | def get_currency_id(self, cr, uid, picking): | 1195 | def get_currency_id(self, cr, uid, picking): |
1544 | 1196 | return False | 1196 | if picking.sale_id: |
1545 | 1197 | return picking.sale_id.pricelist_id.currency_id.id | ||
1546 | 1198 | else: | ||
1547 | 1199 | if picking.purchase_id: | ||
1548 | 1200 | return picking.purchase_id.pricelist_id.currency_id.id | ||
1549 | 1201 | else: | ||
1550 | 1202 | return False | ||
1551 | 1197 | 1203 | ||
1552 | 1198 | def _get_payment_term(self, cr, uid, picking): | 1204 | def _get_payment_term(self, cr, uid, picking): |
1553 | 1199 | """ Gets payment term from partner. | 1205 | """ Gets payment term from partner. |
1554 | 1200 | @return: Payment term | 1206 | @return: Payment term |
1555 | 1201 | """ | 1207 | """ |
1556 | 1208 | if picking.sale_id and picking.sale_id.payment_term: | ||
1557 | 1209 | return picking.sale_id.payment_term.id | ||
1558 | 1202 | partner = picking.address_id.partner_id | 1210 | partner = picking.address_id.partner_id |
1559 | 1203 | return partner.property_payment_term and partner.property_payment_term.id or False | 1211 | return partner.property_payment_term and partner.property_payment_term.id or False |
1560 | 1204 | 1212 | ||
1561 | @@ -1206,37 +1214,85 @@ | |||
1562 | 1206 | """ Gets invoice address of a partner | 1214 | """ Gets invoice address of a partner |
1563 | 1207 | @return {'contact': address, 'invoice': address} for invoice | 1215 | @return {'contact': address, 'invoice': address} for invoice |
1564 | 1208 | """ | 1216 | """ |
1565 | 1217 | res = {} | ||
1566 | 1209 | partner_obj = self.pool.get('res.partner') | 1218 | partner_obj = self.pool.get('res.partner') |
1567 | 1219 | if picking.sale_id: | ||
1568 | 1220 | res['contact'] = picking.sale_id.partner_order_id.id | ||
1569 | 1221 | res['invoice'] = picking.sale_id.partner_invoice_id.id | ||
1570 | 1222 | return res | ||
1571 | 1210 | partner = picking.address_id.partner_id | 1223 | partner = picking.address_id.partner_id |
1574 | 1211 | return partner_obj.address_get(cr, uid, [partner.id], | 1224 | res = partner_obj.address_get(cr, uid, [partner.id], ['contact', 'invoice']) |
1575 | 1212 | ['contact', 'invoice']) | 1225 | if picking.purchase_id: |
1576 | 1226 | partner = picking.purchase_id.partner_id or picking.address_id.partner_id | ||
1577 | 1227 | data = partner_obj.address_get(cr, uid, [partner.id], ['contact', 'invoice']) | ||
1578 | 1228 | res.update(data) | ||
1579 | 1229 | return res | ||
1580 | 1213 | 1230 | ||
1581 | 1214 | def _get_comment_invoice(self, cr, uid, picking): | 1231 | def _get_comment_invoice(self, cr, uid, picking): |
1582 | 1215 | """ | 1232 | """ |
1583 | 1216 | @return: comment string for invoice | 1233 | @return: comment string for invoice |
1584 | 1217 | """ | 1234 | """ |
1585 | 1235 | if picking.note or (picking.sale_id and picking.sale_id.note): | ||
1586 | 1236 | return picking.note or picking.sale_id.note | ||
1587 | 1237 | if picking.purchase_id and picking.purchase_id.notes: | ||
1588 | 1238 | if picking.note: | ||
1589 | 1239 | return picking.note + '\n' + picking.purchase_id.notes | ||
1590 | 1240 | else: | ||
1591 | 1241 | return picking.purchase_id.notes | ||
1592 | 1218 | return picking.note or '' | 1242 | return picking.note or '' |
1593 | 1219 | 1243 | ||
1594 | 1220 | def _get_price_unit_invoice(self, cr, uid, move_line, type, context=None): | 1244 | def _get_price_unit_invoice(self, cr, uid, move_line, type, context=None): |
1595 | 1221 | """ Gets price unit for invoice | 1245 | """ Gets price unit for invoice |
1596 | 1246 | Updates the Unit price according to the UoM received and the UoM ordered | ||
1597 | 1222 | @param move_line: Stock move lines | 1247 | @param move_line: Stock move lines |
1598 | 1223 | @param type: Type of invoice | 1248 | @param type: Type of invoice |
1599 | 1224 | @return: The price unit for the move line | 1249 | @return: The price unit for the move line |
1600 | 1225 | """ | 1250 | """ |
1601 | 1226 | if context is None: | 1251 | if context is None: |
1602 | 1227 | context = {} | 1252 | context = {} |
1611 | 1228 | 1253 | res = None | |
1612 | 1229 | if type in ('in_invoice', 'in_refund'): | 1254 | if move_line.sale_line_id and move_line.sale_line_id.product_id.id == move_line.product_id.id: |
1613 | 1230 | # Take the user company and pricetype | 1255 | uom_id = move_line.product_id.uom_id.id |
1614 | 1231 | context['currency_id'] = move_line.company_id.currency_id.id | 1256 | uos_id = move_line.product_id.uos_id and move_line.product_id.uos_id.id or False |
1615 | 1232 | amount_unit = move_line.product_id.price_get('standard_price', context)[move_line.product_id.id] | 1257 | price = move_line.sale_line_id.price_unit |
1616 | 1233 | return amount_unit | 1258 | coeff = move_line.product_id.uos_coeff |
1617 | 1234 | else: | 1259 | if uom_id != uos_id and coeff != 0: |
1618 | 1235 | return move_line.product_id.list_price | 1260 | price_unit = price / coeff |
1619 | 1261 | res = price_unit | ||
1620 | 1262 | else: | ||
1621 | 1263 | res = move_line.sale_line_id.price_unit | ||
1622 | 1264 | if res is None: | ||
1623 | 1265 | if move_line.purchase_line_id: | ||
1624 | 1266 | res = move_line.purchase_line_id.price_unit | ||
1625 | 1267 | else: | ||
1626 | 1268 | if type in ('in_invoice', 'in_refund'): | ||
1627 | 1269 | # Take the user company and pricetype | ||
1628 | 1270 | context['currency_id'] = move_line.company_id.currency_id.id | ||
1629 | 1271 | amount_unit = move_line.product_id.price_get('standard_price', context)[move_line.product_id.id] | ||
1630 | 1272 | res = amount_unit | ||
1631 | 1273 | else: | ||
1632 | 1274 | res = move_line.product_id.list_price | ||
1633 | 1275 | if type == 'in_refund': | ||
1634 | 1276 | if move_line.picking_id and move_line.picking_id.purchase_id: | ||
1635 | 1277 | po_line_obj = self.pool.get('purchase.order.line') | ||
1636 | 1278 | po_line_id = po_line_obj.search(cr, uid, [('order_id', '=', move_line.picking_id.purchase_id.id), | ||
1637 | 1279 | ('product_id', '=', move_line.product_id.id), | ||
1638 | 1280 | ('state', '!=', 'cancel') | ||
1639 | 1281 | ], limit=1) | ||
1640 | 1282 | if po_line_id: | ||
1641 | 1283 | return po_line_obj.read(cr, uid, po_line_id[0], ['price_unit'])['price_unit'] | ||
1642 | 1284 | if move_line.purchase_line_id: | ||
1643 | 1285 | po_uom_id = move_line.purchase_line_id.product_uom.id | ||
1644 | 1286 | move_uom_id = move_line.product_uom.id | ||
1645 | 1287 | uom_ratio = self.pool.get('product.uom')._compute_price(cr, uid, move_uom_id, 1, po_uom_id) | ||
1646 | 1288 | return res / uom_ratio | ||
1647 | 1289 | return res | ||
1648 | 1236 | 1290 | ||
1649 | 1237 | def _get_discount_invoice(self, cr, uid, move_line): | 1291 | def _get_discount_invoice(self, cr, uid, move_line): |
1650 | 1238 | '''Return the discount for the move line''' | 1292 | '''Return the discount for the move line''' |
1652 | 1239 | return 0.0 | 1293 | if move_line.sale_line_id: |
1653 | 1294 | return move_line.sale_line_id.discount | ||
1654 | 1295 | return 0.0 # including if move_line.purchase_line_id | ||
1655 | 1240 | 1296 | ||
1656 | 1241 | def _get_taxes_invoice(self, cr, uid, move_line, type): | 1297 | def _get_taxes_invoice(self, cr, uid, move_line, type): |
1657 | 1242 | """ Gets taxes on invoice | 1298 | """ Gets taxes on invoice |
1658 | @@ -1244,6 +1300,10 @@ | |||
1659 | 1244 | @param type: Type of invoice | 1300 | @param type: Type of invoice |
1660 | 1245 | @return: Taxes Ids for the move line | 1301 | @return: Taxes Ids for the move line |
1661 | 1246 | """ | 1302 | """ |
1662 | 1303 | if move_line.sale_line_id and move_line.sale_line_id.product_id.id == move_line.product_id.id: | ||
1663 | 1304 | return [x.id for x in move_line.sale_line_id.tax_id] | ||
1664 | 1305 | if move_line.purchase_line_id: | ||
1665 | 1306 | return [x.id for x in move_line.purchase_line_id.taxes_id] | ||
1666 | 1247 | if type in ('in_invoice', 'in_refund'): | 1307 | if type in ('in_invoice', 'in_refund'): |
1667 | 1248 | taxes = move_line.product_id.supplier_taxes_id | 1308 | taxes = move_line.product_id.supplier_taxes_id |
1668 | 1249 | else: | 1309 | else: |
1669 | @@ -1259,7 +1319,11 @@ | |||
1670 | 1259 | else: | 1319 | else: |
1671 | 1260 | return map(lambda x: x.id, taxes) | 1320 | return map(lambda x: x.id, taxes) |
1672 | 1261 | 1321 | ||
1674 | 1262 | def _get_account_analytic_invoice(self, cr, uid, picking, move_line): | 1322 | def _get_account_analytic_invoice(self, picking, move_line): |
1675 | 1323 | if picking.sale_id: | ||
1676 | 1324 | return picking.sale_id.project_id.id | ||
1677 | 1325 | if move_line.purchase_line_id: | ||
1678 | 1326 | return move_line.purchase_line_id.account_analytic_id.id | ||
1679 | 1263 | return False | 1327 | return False |
1680 | 1264 | 1328 | ||
1681 | 1265 | def _invoice_line_hook(self, cr, uid, move_line, invoice_line_id, account_id): | 1329 | def _invoice_line_hook(self, cr, uid, move_line, invoice_line_id, account_id): |
1682 | @@ -1294,9 +1358,33 @@ | |||
1683 | 1294 | return True | 1358 | return True |
1684 | 1295 | 1359 | ||
1685 | 1296 | def _invoice_hook(self, cr, uid, picking, invoice_id): | 1360 | def _invoice_hook(self, cr, uid, picking, invoice_id): |
1687 | 1297 | '''Call after the creation of the invoice''' | 1361 | """ |
1688 | 1362 | Create a link between invoice and purchase_order. | ||
1689 | 1363 | Copy analytic distribution from purchase order to invoice (or from commitment voucher if it exists) | ||
1690 | 1364 | |||
1691 | 1365 | To call after the creation of the invoice | ||
1692 | 1366 | """ | ||
1693 | 1367 | sale_obj = self.pool.get('sale.order') | ||
1694 | 1368 | purchase_obj = self.pool.get('purchase.order') | ||
1695 | 1369 | if invoice_id and picking: | ||
1696 | 1370 | po_id = picking.purchase_id and picking.purchase_id.id or False | ||
1697 | 1371 | so_id = picking.sale_id and picking.sale_id.id or False | ||
1698 | 1372 | if po_id: | ||
1699 | 1373 | self.pool.get('purchase.order').write(cr, uid, [po_id], {'invoice_ids': [(4, invoice_id)]}) | ||
1700 | 1374 | if so_id: | ||
1701 | 1375 | self.pool.get('sale.order').write(cr, uid, [so_id], {'invoice_ids': [(4, invoice_id)]}) | ||
1702 | 1376 | # Copy analytic distribution from purchase order or commitment voucher (if it exists) or sale order | ||
1703 | 1377 | self.pool.get('account.invoice').fetch_analytic_distribution(cr, uid, [invoice_id]) | ||
1704 | 1378 | if picking.sale_id: | ||
1705 | 1379 | sale_obj.write(cr, uid, [picking.sale_id.id], { | ||
1706 | 1380 | 'invoice_ids': [(4, invoice_id)], | ||
1707 | 1381 | }) | ||
1708 | 1382 | if picking.purchase_id: | ||
1709 | 1383 | purchase_obj.write(cr, uid, [picking.purchase_id.id], {'invoice_id': invoice_id, }) | ||
1710 | 1298 | return | 1384 | return |
1711 | 1299 | 1385 | ||
1712 | 1386 | # action_invoice_create method has been removed because of the impossibility to retrieve DESTINATION from SO. | ||
1713 | 1387 | |||
1714 | 1300 | def _get_invoice_type(self, pick): | 1388 | def _get_invoice_type(self, pick): |
1715 | 1301 | src_usage = dest_usage = None | 1389 | src_usage = dest_usage = None |
1716 | 1302 | inv_type = None | 1390 | inv_type = None |
1717 | @@ -1550,12 +1638,17 @@ | |||
1718 | 1550 | else: | 1638 | else: |
1719 | 1551 | name = move_line.name | 1639 | name = move_line.name |
1720 | 1552 | 1640 | ||
1721 | 1641 | cv_line = move_line and move_line.purchase_line_id and move_line.purchase_line_id.cv_line_ids and \ | ||
1722 | 1642 | move_line.purchase_line_id.cv_line_ids[0] or False | ||
1723 | 1643 | cv_version = cv_line and cv_line.commit_id and cv_line.commit_id.version or 1 | ||
1724 | 1553 | if inv_type in ('out_invoice', 'out_refund'): | 1644 | if inv_type in ('out_invoice', 'out_refund'): |
1725 | 1554 | account_id = move_line.product_id.product_tmpl_id.\ | 1645 | account_id = move_line.product_id.product_tmpl_id.\ |
1726 | 1555 | property_account_income.id | 1646 | property_account_income.id |
1727 | 1556 | if not account_id: | 1647 | if not account_id: |
1728 | 1557 | account_id = move_line.product_id.categ_id.\ | 1648 | account_id = move_line.product_id.categ_id.\ |
1729 | 1558 | property_account_income_categ.id | 1649 | property_account_income_categ.id |
1730 | 1650 | elif cv_version > 1: | ||
1731 | 1651 | account_id = cv_line.account_id.id | ||
1732 | 1559 | else: | 1652 | else: |
1733 | 1560 | account_id = move_line.product_id.product_tmpl_id.\ | 1653 | account_id = move_line.product_id.product_tmpl_id.\ |
1734 | 1561 | property_account_expense.id | 1654 | property_account_expense.id |
1735 | @@ -1567,14 +1660,15 @@ | |||
1736 | 1567 | move_line, inv_type) | 1660 | move_line, inv_type) |
1737 | 1568 | discount = self._get_discount_invoice(cr, uid, move_line) | 1661 | discount = self._get_discount_invoice(cr, uid, move_line) |
1738 | 1569 | tax_ids = self._get_taxes_invoice(cr, uid, move_line, inv_type) | 1662 | tax_ids = self._get_taxes_invoice(cr, uid, move_line, inv_type) |
1740 | 1570 | account_analytic_id = self._get_account_analytic_invoice(cr, uid, picking, move_line) | 1663 | account_analytic_id = self._get_account_analytic_invoice(picking, move_line) |
1741 | 1571 | 1664 | ||
1742 | 1572 | #set UoS if it's a sale and the picking doesn't have one | 1665 | #set UoS if it's a sale and the picking doesn't have one |
1743 | 1573 | uos_id = move_line.product_uos and move_line.product_uos.id or False | 1666 | uos_id = move_line.product_uos and move_line.product_uos.id or False |
1744 | 1574 | if not uos_id and inv_type in ('out_invoice', 'out_refund'): | 1667 | if not uos_id and inv_type in ('out_invoice', 'out_refund'): |
1745 | 1575 | uos_id = move_line.product_uom.id | 1668 | uos_id = move_line.product_uom.id |
1748 | 1576 | account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position, account_id) | 1669 | if cv_version < 2: |
1749 | 1577 | invoice_line_id = invoice_line_obj.create(cr, uid, { | 1670 | account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position, account_id) |
1750 | 1671 | inv_vals = { | ||
1751 | 1578 | 'name': name, | 1672 | 'name': name, |
1752 | 1579 | 'origin': origin, | 1673 | 'origin': origin, |
1753 | 1580 | 'invoice_id': invoice_id, | 1674 | 'invoice_id': invoice_id, |
1754 | @@ -1586,7 +1680,10 @@ | |||
1755 | 1586 | 'quantity': move_line.product_uos_qty or move_line.product_qty, | 1680 | 'quantity': move_line.product_uos_qty or move_line.product_qty, |
1756 | 1587 | 'invoice_line_tax_id': [(6, 0, tax_ids)], | 1681 | 'invoice_line_tax_id': [(6, 0, tax_ids)], |
1757 | 1588 | 'account_analytic_id': account_analytic_id, | 1682 | 'account_analytic_id': account_analytic_id, |
1759 | 1589 | }, context=context) | 1683 | } |
1760 | 1684 | if cv_version > 1: | ||
1761 | 1685 | inv_vals.update({'cv_line_ids': [(4, cv_line.id)],}) | ||
1762 | 1686 | invoice_line_id = invoice_line_obj.create(cr, uid, inv_vals, context=context) | ||
1763 | 1590 | self._invoice_line_hook(cr, uid, move_line, invoice_line_id, account_id) | 1687 | self._invoice_line_hook(cr, uid, move_line, invoice_line_id, account_id) |
1764 | 1591 | 1688 | ||
1765 | 1592 | if picking.sale_id: | 1689 | if picking.sale_id: |
1766 | @@ -1613,7 +1710,7 @@ | |||
1767 | 1613 | tax_ids = sale_line.tax_id | 1710 | tax_ids = sale_line.tax_id |
1768 | 1614 | tax_ids = map(lambda x: x.id, tax_ids) | 1711 | tax_ids = map(lambda x: x.id, tax_ids) |
1769 | 1615 | 1712 | ||
1771 | 1616 | account_analytic_id = self._get_account_analytic_invoice(cr, uid, picking, sale_line) | 1713 | account_analytic_id = self._get_account_analytic_invoice(picking, sale_line) |
1772 | 1617 | 1714 | ||
1773 | 1618 | account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, picking.sale_id.partner_id.property_account_position, account_id) | 1715 | account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, picking.sale_id.partner_id.property_account_position, account_id) |
1774 | 1619 | invoice_line_id = invoice_line_obj.create(cr, uid, { | 1716 | invoice_line_id = invoice_line_obj.create(cr, uid, { |
1775 | 1620 | 1717 | ||
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 | 870 | 870 | ||
1782 | 871 | return res | 871 | return res |
1783 | 872 | 872 | ||
1784 | 873 | def _get_price_unit_invoice(self, cr, uid, move_line, type): | ||
1785 | 874 | ''' | ||
1786 | 875 | Update the Unit price according to the UoM received and the UoM ordered | ||
1787 | 876 | ''' | ||
1788 | 877 | res = super(stock_picking, self)._get_price_unit_invoice(cr, uid, move_line, type) | ||
1789 | 878 | if type == 'in_refund': | ||
1790 | 879 | if move_line.picking_id and move_line.picking_id.purchase_id: | ||
1791 | 880 | po_line_obj = self.pool.get('purchase.order.line') | ||
1792 | 881 | po_line_id = po_line_obj.search(cr, uid, [('order_id', '=', move_line.picking_id.purchase_id.id), | ||
1793 | 882 | ('product_id', '=', move_line.product_id.id), | ||
1794 | 883 | ('state', '!=', 'cancel') | ||
1795 | 884 | ], limit=1) | ||
1796 | 885 | if po_line_id: | ||
1797 | 886 | return po_line_obj.read(cr, uid, po_line_id[0], ['price_unit'])['price_unit'] | ||
1798 | 887 | |||
1799 | 888 | if move_line.purchase_line_id: | ||
1800 | 889 | po_uom_id = move_line.purchase_line_id.product_uom.id | ||
1801 | 890 | move_uom_id = move_line.product_uom.id | ||
1802 | 891 | uom_ratio = self.pool.get('product.uom')._compute_price(cr, uid, move_uom_id, 1, po_uom_id) | ||
1803 | 892 | return res / uom_ratio | ||
1804 | 893 | |||
1805 | 894 | return res | ||
1806 | 895 | |||
1807 | 896 | def action_confirm(self, cr, uid, ids, context=None): | 873 | def action_confirm(self, cr, uid, ids, context=None): |
1808 | 897 | """ | 874 | """ |
1809 | 898 | stock.picking: action confirm | 875 | stock.picking: action confirm |
1810 | 899 | 876 | ||
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 | 332 | kind = 'update' | 332 | kind = 'update' |
1818 | 333 | pol_to_update = [pol_updated] | 333 | pol_to_update = [pol_updated] |
1819 | 334 | confirmed_sequence = self.pool.get('purchase.order.line.state').get_sequence(cr, uid, [], 'confirmed', context=context) | 334 | confirmed_sequence = self.pool.get('purchase.order.line.state').get_sequence(cr, uid, [], 'confirmed', context=context) |
1821 | 335 | po_line = self.browse(cr, uid, pol_updated, fields_to_fetch=['state', 'product_qty'], context=context) | 335 | po_line = self.browse(cr, uid, pol_updated, fields_to_fetch=['state', 'product_qty', 'price_unit', 'cv_line_ids'], context=context) |
1822 | 336 | pol_state = po_line.state | 336 | pol_state = po_line.state |
1823 | 337 | if sol_dict['state'] in ['cancel', 'cancel_r']: | 337 | if sol_dict['state'] in ['cancel', 'cancel_r']: |
1824 | 338 | pol_values['cancelled_by_sync'] = True | 338 | pol_values['cancelled_by_sync'] = True |
1825 | @@ -340,6 +340,12 @@ | |||
1826 | 340 | # if the state is less than confirmed we update the PO line | 340 | # if the state is less than confirmed we update the PO line |
1827 | 341 | if debug: | 341 | if debug: |
1828 | 342 | logger.info("Write pol id: %s, values: %s" % (pol_to_update, pol_values)) | 342 | logger.info("Write pol id: %s, values: %s" % (pol_to_update, pol_values)) |
1829 | 343 | 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 | 344 | # update qty on confirmed po line: update CV line if any | ||
1831 | 345 | # from_cancel = True : do not trigger wkf transition draft -> open | ||
1832 | 346 | self.pool.get('account.invoice')._update_commitments_lines(cr, uid, [po_ids[0]], cvl_amount_dic={ | ||
1833 | 347 | po_line.cv_line_ids[0].id: round((po_line.product_qty - pol_values['product_qty'])*po_line.price_unit, 2) | ||
1834 | 348 | }, from_cancel=True, context=context) | ||
1835 | 343 | self.pool.get('purchase.order.line').write(cr, uid, pol_to_update, pol_values, context=context) | 349 | self.pool.get('purchase.order.line').write(cr, uid, pol_to_update, pol_values, context=context) |
1836 | 344 | 350 | ||
1837 | 345 | if debug: | 351 | if debug: |
1838 | 346 | 352 | ||
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 | 360 | if field.translate: | 360 | if field.translate: |
1846 | 361 | if operator in ('like', 'ilike', 'not like', 'not ilike'): | 361 | if operator in ('like', 'ilike', 'not like', 'not ilike'): |
1847 | 362 | right = '%%%s%%' % right | 362 | right = '%%%s%%' % right |
1848 | 363 | <<<<<<< TREE | ||
1849 | 363 | if right and operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): | 364 | if right and operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): |
1850 | 364 | right = right.replace('\\', '\\\\').replace('_', '\\_') | 365 | right = right.replace('\\', '\\\\').replace('_', '\\_') |
1851 | 366 | ======= | ||
1852 | 367 | if operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): | ||
1853 | 368 | right = right.replace('\\', '\\\\').replace('_', '\\_') | ||
1854 | 369 | >>>>>>> MERGE-SOURCE | ||
1855 | 365 | 370 | ||
1856 | 366 | operator = {'=like':'like','=ilike':'ilike'}.get(operator,operator) | 371 | operator = {'=like':'like','=ilike':'ilike'}.get(operator,operator) |
1857 | 367 | 372 | ||
1858 | @@ -486,8 +491,13 @@ | |||
1859 | 486 | elif left in table._columns: | 491 | elif left in table._columns: |
1860 | 487 | params = table._columns[left]._symbol_set[1](right) | 492 | params = table._columns[left]._symbol_set[1](right) |
1861 | 488 | 493 | ||
1862 | 494 | <<<<<<< TREE | ||
1863 | 489 | if params and operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): | 495 | if params and operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): |
1864 | 490 | params = params.replace('\\', '\\\\').replace('_', '\\_') | 496 | params = params.replace('\\', '\\\\').replace('_', '\\_') |
1865 | 497 | ======= | ||
1866 | 498 | if operator in ('like', 'ilike', 'not like', 'not ilike', '=like', '=ilike'): | ||
1867 | 499 | params = params.replace('\\', '\\\\').replace('_', '\\_') | ||
1868 | 500 | >>>>>>> MERGE-SOURCE | ||
1869 | 491 | if add_null: | 501 | if add_null: |
1870 | 492 | query = '(%s OR %s IS NULL)' % (query, left) | 502 | query = '(%s OR %s IS NULL)' % (query, left) |
1871 | 493 | 503 |