Merge lp:~julie-w/unifield-server/US-8896 into lp:unifield-server

Proposed by jftempo
Status: Merged
Merged at revision: 6155
Proposed branch: lp:~julie-w/unifield-server/US-8896
Merge into: lp:unifield-server
Diff against target: 629 lines (+176/-82)
14 files modified
bin/addons/account/account_invoice_view.xml (+7/-2)
bin/addons/account/invoice.py (+5/-1)
bin/addons/account/report/export_invoice.mako (+1/-1)
bin/addons/account/report/invoice_excel_export.mako (+1/-1)
bin/addons/account_override/account_invoice_sync.py (+61/-18)
bin/addons/account_override/account_invoice_view.xml (+6/-20)
bin/addons/account_override/invoice.py (+16/-3)
bin/addons/account_override/report/allocation_invoices_report.rml (+1/-1)
bin/addons/account_override/report/report_allocation_invoices.py (+30/-6)
bin/addons/analytic_distribution/analytic_distribution.py (+2/-2)
bin/addons/analytic_distribution/invoice.py (+1/-1)
bin/addons/msf_profile/i18n/fr_MF.po (+22/-24)
bin/addons/register_accounting/account_invoice_view.xml (+8/-2)
bin/addons/register_accounting/invoice.py (+15/-0)
To merge this branch: bzr merge lp:~julie-w/unifield-server/US-8896
Reviewer Review Type Date Requested Status
UniField Reviewer Team Pending
Review via email: mp+413758@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/addons/account/account_invoice_view.xml'
2--- bin/addons/account/account_invoice_view.xml 2022-01-05 16:30:54 +0000
3+++ bin/addons/account/account_invoice_view.xml 2022-01-06 16:20:57 +0000
4@@ -83,7 +83,10 @@
5 <field name="discount" groups="base.group_extended"
6 attrs="{'readonly': [('invoice_type', '=', 'in_invoice'), ('line_synced', '=', True)]}"/>
7 <field colspan="4" name="name"/>
8- <field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view')]" name="account_id"/>
9+ <field name="account_id"
10+ domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]"
11+ required="1"
12+ />
13 <field domain="[('type','&lt;&gt;','view'), ('company_id', '=', parent.company_id), ('parent_id', '!=', False)]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
14 <newline/>
15 <field name="company_id" groups="base.group_multi_company" readonly="1"/>
16@@ -236,7 +239,7 @@
17 </group>
18
19 <field colspan="4" default_get="{'check_total': check_total, 'invoice_line': invoice_line, 'address_invoice_id': address_invoice_id, 'partner_id': partner_id, 'price_type': 'price_type' in dir() and price_type or False}" name="invoice_line" nolabel="1">
20- <tree string="Invoice lines" editable="both" colors="red:analytic_distribution_state in ('invalid', 'invalid_small_amount') or inactive_product == True;black:analytic_distribution_state in ('none','valid') and inactive_product == False">
21+ <tree string="Invoice lines" editable="both" colors="red:analytic_distribution_state in ('invalid', 'invalid_small_amount') or inactive_product == True or not account_id;black:analytic_distribution_state in ('none', 'valid') and inactive_product == False and account_id">
22 <field name="from_supply" invisible="1"/>
23 <field name="line_synced" invisible="1"/>
24 <field name="invoice_type" invisible="1"/>
25@@ -252,6 +255,7 @@
26 />
27 <field name="account_id"
28 domain="[('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]"
29+ required="1"
30 />
31 <field name="invoice_line_tax_id" view_mode="2" context="{'type':parent.type}" domain="[('parent_id','=',False)]"/>
32 <field name="inactive_product" invisible="1" />
33@@ -489,6 +493,7 @@
34 />
35 <field name="account_id"
36 domain="[('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]"
37+ required="1"
38 context="{'type': parent.type, 'journal_type': parent.journal_type}"
39 />
40 <button name="button_analytic_distribution" string="Analytical Distribution" type="object"
41
42=== modified file 'bin/addons/account/invoice.py'
43--- bin/addons/account/invoice.py 2022-01-05 16:25:16 +0000
44+++ bin/addons/account/invoice.py 2022-01-06 16:20:57 +0000
45@@ -1338,6 +1338,9 @@
46 if 'analytic_line_ids' in line:
47 line['analytic_line_ids'] = False
48
49+ if 'allow_no_account' in line:
50+ line['allow_no_account'] = False
51+
52 for field in (
53 'company_id', 'partner_id', 'account_id', 'product_id',
54 'uos_id', 'account_analytic_id', 'tax_code_id', 'base_code_id','account_tax_id',
55@@ -1660,7 +1663,8 @@
56 selection=PARTNER_TYPE, readonly=True, store=False),
57 'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null'),
58 'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
59- 'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."),
60+ 'account_id': fields.many2one('account.account', 'Account', domain=[('type', '<>', 'view'), ('type', '<>', 'closed')],
61+ help="The income or expense account related to the selected product."),
62 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Account')),
63 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal', type="float",
64 digits_compute= dp.get_precision('Account'), store=True),
65
66=== modified file 'bin/addons/account/report/export_invoice.mako'
67--- bin/addons/account/report/export_invoice.mako 2021-04-28 14:17:59 +0000
68+++ bin/addons/account/report/export_invoice.mako 2022-01-06 16:20:57 +0000
69@@ -145,7 +145,7 @@
70 % endif
71 <Data ss:Type="String">${inv_line.product_id and inv_line.product_id.default_code or ''|x}</Data></Cell>
72
73- <Cell ss:StyleID="editable"><Data ss:Type="String">${inv_line.account_id.code|x}</Data></Cell>
74+ <Cell ss:StyleID="editable"><Data ss:Type="String">${inv_line.account_id and inv_line.account_id.code or ''|x}</Data></Cell>
75
76 % if is_ro:
77 <Cell ss:StyleID="non_editable_number">
78
79=== modified file 'bin/addons/account/report/invoice_excel_export.mako'
80--- bin/addons/account/report/invoice_excel_export.mako 2020-04-14 16:07:03 +0000
81+++ bin/addons/account/report/invoice_excel_export.mako 2022-01-06 16:20:57 +0000
82@@ -115,7 +115,7 @@
83 <Cell ss:StyleID="line_number"><Data ss:Type="Number">${distrib_line['percentage']|x}</Data></Cell>
84 <Cell ss:StyleID="line_number"><Data ss:Type="Number">${inv_line.price_unit|x}</Data></Cell>
85 <Cell ss:StyleID="line_number"><Data ss:Type="Number">${distrib_line['subtotal']|x}</Data></Cell>
86- <Cell ss:StyleID="line"><Data ss:Type="String">${inv_line.account_id.code|x}</Data></Cell>
87+ <Cell ss:StyleID="line"><Data ss:Type="String">${inv_line.account_id and inv_line.account_id.code or ''|x}</Data></Cell>
88 <Cell ss:StyleID="line"><Data ss:Type="String">${distrib_line['cost_center']|x}</Data></Cell>
89 <Cell ss:StyleID="line"><Data ss:Type="String">${distrib_line['destination']|x}</Data></Cell>
90 <Cell ss:StyleID="line"><Data ss:Type="String">${inv_line.invoice_id.number or ''|x}</Data></Cell>
91
92=== modified file 'bin/addons/account_override/account_invoice_sync.py'
93--- bin/addons/account_override/account_invoice_sync.py 2021-10-18 08:18:07 +0000
94+++ bin/addons/account_override/account_invoice_sync.py 2022-01-06 16:20:57 +0000
95@@ -84,6 +84,21 @@
96 fp_distrib_line_obj.create(cr, uid, distrib_vals, context=context)
97 vals.update({'analytic_distribution_id': distrib_id,})
98
99+ def _set_partially_run(self, line_name, partially_run_msg, new_msg, context):
100+ """
101+ Sets the invoices in Partially (Not) Run:
102+ - updates contexts accordingly
103+ - updates the partially_run_msg with the new_msg
104+ - at line level sets the account to False and the tag allow_no_account to True
105+ """
106+ context['partial_sync_run'] = True
107+ line_account_id = False
108+ allow_no_account = True
109+ if partially_run_msg:
110+ partially_run_msg += "\n"
111+ partially_run_msg += 'Line "%s": %s' % (line_name, new_msg)
112+ return line_account_id, allow_no_account, partially_run_msg
113+
114 def _create_invoice_lines(self, cr, uid, inv_lines_data, inv_id, inv_posting_date, inv_linked_po, from_supply, context=None):
115 """
116 Creates the lines of the automatic counterpart invoice (inv_id) generated at synchro time.
117@@ -96,7 +111,9 @@
118 product_uom_obj = self.pool.get('product.uom')
119 inv_line_obj = self.pool.get('account.invoice.line')
120 pol_obj = self.pool.get('purchase.order.line')
121+ partially_run_msg = ""
122 for inv_line in inv_lines_data:
123+ allow_no_account = False
124 line_name = inv_line.get('name', '')
125 if not line_name: # required field
126 raise osv.except_osv(_('Error'), _("Impossible to retrieve the line description."))
127@@ -153,22 +170,51 @@
128 elif not line_account_id:
129 account_code = inv_line.get('account_id', {}).get('code', '')
130 if not account_code:
131- raise osv.except_osv(_('Error'), _("Impossible to retrieve the account code at line level."))
132- account_ids = account_obj.search(cr, uid, [('code', '=', account_code)], limit=1, context=context)
133- if not account_ids:
134- raise osv.except_osv(_('Error'), _("Account code %s not found.") % account_code)
135- line_account_id = account_ids[0]
136- if not line_account_id:
137- raise osv.except_osv(_('Error'), _("Error when retrieving the account at line level."))
138- line_account = account_obj.browse(cr, uid, line_account_id,
139- fields_to_fetch=['activation_date', 'inactivation_date'], context=context)
140- if inv_posting_date < line_account.activation_date or \
141- (line_account.inactivation_date and inv_posting_date >= line_account.inactivation_date):
142- raise osv.except_osv(_('Error'), _('The account "%s - %s" is inactive.') % (line_account.code, line_account.name))
143- inv_line_vals.update({'account_id': line_account_id,
144+ new_msg = "Impossible to retrieve the account code."
145+ line_account_id, allow_no_account, partially_run_msg = self._set_partially_run(line_name,
146+ partially_run_msg,
147+ new_msg, context)
148+ else:
149+ account_ids = account_obj.search(cr, uid, [('code', '=', account_code)], limit=1, context=context)
150+ if not account_ids:
151+ new_msg = "Account %s not found." % (account_code,)
152+ line_account_id, allow_no_account, partially_run_msg = self._set_partially_run(line_name, partially_run_msg,
153+ new_msg, context)
154+ else:
155+ line_account_id = account_ids[0]
156+ if not line_account_id and not allow_no_account:
157+ new_msg = "Error when retrieving the account."
158+ line_account_id, allow_no_account, partially_run_msg = self._set_partially_run(line_name, partially_run_msg, new_msg, context)
159+ if line_account_id:
160+ line_account = account_obj.browse(cr, uid, line_account_id,
161+ fields_to_fetch=['activation_date', 'inactivation_date', 'code'], context=context)
162+ if inv_posting_date < line_account.activation_date or \
163+ (line_account.inactivation_date and inv_posting_date >= line_account.inactivation_date):
164+ new_msg = "Account %s inactive." % (line_account.code,)
165+ line_account_id, allow_no_account, partially_run_msg = self._set_partially_run(line_name, partially_run_msg,
166+ new_msg, context)
167+ inv_line_vals.update({'account_id': line_account_id or False,
168+ 'allow_no_account': allow_no_account,
169 'product_id': product_id,
170 })
171 inv_line_obj.create(cr, uid, inv_line_vals, context=context)
172+ return partially_run_msg
173+
174+ def _get_msg(self, journal_type, partially_run_msg, inv_id):
175+ """
176+ Returns the message to be printed in the Messages Received
177+ """
178+ if journal_type == 'sale':
179+ msg_prefix = 'The ISI No.'
180+ elif journal_type == 'intermission':
181+ msg_prefix = 'The IVI No.'
182+ else:
183+ msg_prefix = 'The Invoice No.'
184+ if partially_run_msg:
185+ msg_suffix = "is Partially Not Run.\n\n%s" % partially_run_msg
186+ else:
187+ msg_suffix = "has been created successfully."
188+ return "%s %s %s" % (msg_prefix, inv_id, msg_suffix)
189
190 def create_invoice_from_sync(self, cr, uid, source, invoice_data, context=None):
191 """
192@@ -323,11 +369,8 @@
193 )
194 inv_id = self.create(cr, uid, vals, context=context)
195 if inv_id:
196- self._create_invoice_lines(cr, uid, inv_lines, inv_id, posting_date, po, from_supply, context=context)
197- if journal_type == 'sale':
198- msg = "ISI No. %s created successfully." % inv_id
199- elif journal_type == 'intermission':
200- msg = "IVI No. %s created successfully." % inv_id
201+ partially_run_msg = self._create_invoice_lines(cr, uid, inv_lines, inv_id, posting_date, po, from_supply, context=context)
202+ msg = self._get_msg(journal_type, partially_run_msg, inv_id)
203 self._logger.info(msg)
204 return msg
205
206
207=== modified file 'bin/addons/account_override/account_invoice_view.xml'
208--- bin/addons/account_override/account_invoice_view.xml 2022-01-05 16:28:33 +0000
209+++ bin/addons/account_override/account_invoice_view.xml 2022-01-06 16:20:57 +0000
210@@ -28,21 +28,6 @@
211 </record>
212
213
214- <!-- Change account_id domain -->
215- <record id="invoice_line_form3" model="ir.ui.view">
216- <field name="name">account.invoice.line.form</field>
217- <field name="model">account.invoice.line</field>
218- <field name="type">form</field>
219- <field name="priority" eval="20"/>
220- <field name="inherit_id" ref="account.view_invoice_line_form"/>
221- <field name="arch" type="xml">
222- <xpath expr="//field[@name='account_id']" position="attributes">
223- <attribute name="domain">[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]</attribute>
224- </xpath>
225- </field>
226- </record>
227-
228-
229 <!--
230 Debit Note views
231 -->
232@@ -104,7 +89,7 @@
233 <field colspan="4" name="invoice_line" nolabel="1" widget="one2many_list">
234 <tree editable="top" string="Invoice Line" noteditable="1" hide_new_button="1">
235 <field name="name"/>
236- <field name="account_id" domain="[('restricted_area', '=', 'invoice_lines')]"/>
237+ <field name="account_id" domain="[('restricted_area', '=', 'invoice_lines')]" required="1"/>
238 <field name="quantity"/>
239 <field name="uos_id" string="UoM"/>
240 <field name="price_unit"/>
241@@ -236,7 +221,7 @@
242 attrs="{'readonly': [('from_supply', '=', True)]}"
243 on_change="onchange_donation_product(product_id, quantity, parent.currency_id, context)"
244 />
245- <field name="account_id" domain="[('restricted_area', '=', 'donation_lines')]"/>
246+ <field name="account_id" domain="[('restricted_area', '=', 'donation_lines')]" required="1"/>
247 <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-stock_symbol-selection"
248 context="{'d_journal_id': parent.journal_id , 'd_partner_id': parent.partner_id, 'd_address_invoice_id':parent.address_invoice_id,'d_date_invoice': parent.date_invoice, 'd_register_posting_date': parent.register_posting_date, 'd_account_id': parent.account_id, 'd_partner_bank_id': parent.partner_bank_id, 'd_payment_term': parent.payment_term, 'd_name': parent.name, 'd_origine': parent.origin, 'd_address_contact_id': parent.address_contact_id, 'd_user_id': parent.user_id, 'd_comment': parent.comment}"
249 />
250@@ -260,7 +245,7 @@
251 on_change="onchange_donation_product(product_id, quantity, parent.currency_id, context)"
252 default_focus="1"
253 />
254- <field name="account_id" domain="[('restricted_area', '=', 'donation_lines')]"/>
255+ <field name="account_id" domain="[('restricted_area', '=', 'donation_lines')]" required="1"/>
256 <field name="name" colspan="4" />
257 <field name="quantity"
258 attrs="{'readonly': [('from_supply', '=', True)]}"
259@@ -405,7 +390,7 @@
260 <field colspan="4" name="invoice_line" nolabel="1" widget="one2many_list" context="{'is_intermission': True, 'from_inv_form': True}">
261 <tree string="Intermission Voucher Lines"
262 editable="both"
263- colors="red:analytic_distribution_state in ('invalid', 'invalid_small_amount') or inactive_product == True;black:inactive_product == False and analytic_distribution_state in ('none', 'valid')">
264+ colors="red:analytic_distribution_state in ('invalid', 'invalid_small_amount') or inactive_product == True or not account_id;black:inactive_product == False and analytic_distribution_state in ('none', 'valid') and account_id">
265 <field name="from_supply" invisible="1"/>
266 <field name="line_synced" invisible="1"/>
267 <field name="invoice_type" invisible="1"/>
268@@ -418,6 +403,7 @@
269 ('from_supply', '=', True),
270 '&amp;', ('invoice_type', '=', 'in_invoice'), ('line_synced', '=', True)]}" />
271 <field name="account_id" domain="[('restricted_area', '=', 'intermission_lines')]"
272+ required="1"
273 context="{'type': parent.type, 'journal_type': parent.journal_type, 'intermission_type': parent.type == 'out_invoice' and 'out' or 'in'}"
274 />
275 <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-stock_symbol-selection"
276@@ -468,7 +454,7 @@
277 <field name="price_unit" attrs="{'readonly': [('invoice_type', '=', 'in_invoice'), ('line_synced', '=', True)]}"/>
278 <field name="discount" groups="base.group_extended" attrs="{'readonly': [('invoice_type', '=', 'in_invoice'), ('line_synced', '=', True)]}"/>
279 <field name="name" colspan="4"/>
280- <field name="account_id" domain="[('restricted_area', '=', 'intermission_lines')]"/>
281+ <field name="account_id" domain="[('restricted_area', '=', 'intermission_lines')]" required="1"/>
282 <group colspan="6">
283 <field name="analytic_distribution_state" invisible="1"/>
284 <field name="newline" invisible="1" />
285
286=== modified file 'bin/addons/account_override/invoice.py'
287--- bin/addons/account_override/invoice.py 2022-01-05 16:30:54 +0000
288+++ bin/addons/account_override/invoice.py 2022-01-06 16:20:57 +0000
289@@ -1640,6 +1640,7 @@
290 # - an invoice line can be linked to several CV lines => e.g. merge invoice lines by account
291 'cv_line_ids': fields.many2many('account.commitment.line', 'inv_line_cv_line_rel', 'inv_line_id', 'cv_line_id',
292 string='Commitment Voucher Lines'),
293+ 'allow_no_account': fields.boolean(string='Allow an empty account on the line', readonly=True),
294 }
295
296 _defaults = {
297@@ -1647,10 +1648,16 @@
298 'is_corrected': lambda *a: False,
299 'vat_ok': lambda obj, cr, uid, context: obj.pool.get('unifield.setup.configuration').get_config(cr, uid).vat_ok,
300 'merged_line': lambda *a: False,
301+ 'allow_no_account': lambda *a: False,
302 }
303
304 _order = 'line_number'
305
306+ _sql_constraints = [
307+ ('ck_invl_account', "CHECK(account_id IS NOT NULL OR COALESCE(allow_no_account, 'f') = 't')",
308+ 'The invoice lines must have an account.')
309+ ]
310+
311 def _check_on_invoice_line_big_amounts(self, cr, uid, ids, context=None):
312 """
313 Prevents booking amounts having more than 10 digits before the comma, i.e. amounts starting from 10 billions.
314@@ -1782,20 +1789,26 @@
315
316 return new_id
317
318- def copy_data(self, cr, uid, inv_id, default=None, context=None):
319+ def copy_data(self, cr, uid, invl_id, default=None, context=None):
320 """
321 Copy an invoice line without its move lines,
322 without the link to a reversed invoice line,
323 and without link to PO/FO/CV lines when the duplication is manual
324- Reset the merged_line tag.
325+ Reset the merged_line and allow_no_account tags.
326+ Prevent the manual duplication of invoices lines with no account.
327 """
328 if context is None:
329 context = {}
330 if default is None:
331 default = {}
332+ # The only way to get invoice lines without account should be via synchro and not via duplication
333+ # (display a specific error message instead of the SQL error)
334+ if context.get('from_copy_web') and not self.read(cr, uid, invl_id, ['account_id'], context=context)['account_id']:
335+ raise osv.except_osv(_('Warning'), _("Duplication not allowed. Please set an account on all lines first."))
336 default.update({'move_lines': False,
337 'reversed_invoice_line_id': False,
338 'merged_line': False,
339+ 'allow_no_account': False,
340 })
341 # Manual duplication should generate a "manual document not created through the supply workflow"
342 # so we don't keep the link to PO/FO/CV at line level
343@@ -1807,7 +1820,7 @@
344 'purchase_order_line_ids': [],
345 'cv_line_ids': [(6, 0, [])],
346 })
347- return super(account_invoice_line, self).copy_data(cr, uid, inv_id, default, context)
348+ return super(account_invoice_line, self).copy_data(cr, uid, invl_id, default, context)
349
350 def unlink(self, cr, uid, ids, context=None):
351 """
352
353=== modified file 'bin/addons/account_override/report/allocation_invoices_report.rml'
354--- bin/addons/account_override/report/allocation_invoices_report.rml 2016-04-29 14:08:12 +0000
355+++ bin/addons/account_override/report/allocation_invoices_report.rml 2022-01-06 16:20:57 +0000
356@@ -88,7 +88,7 @@
357 <para style="Information">[[ o.partner_id.name ]]</para>
358 </td>
359 <td>
360- <para style="Information">[[ getSel(o, 'type') ]]</para>
361+ <para style="Information">[[ get_doc_type(o) ]]</para>
362 </td>
363 <td>
364 <para style="Information">[[ get_journal_code(o) ]]</para>
365
366=== modified file 'bin/addons/account_override/report/report_allocation_invoices.py'
367--- bin/addons/account_override/report/report_allocation_invoices.py 2017-08-09 13:31:11 +0000
368+++ bin/addons/account_override/report/report_allocation_invoices.py 2022-01-06 16:20:57 +0000
369@@ -34,6 +34,7 @@
370 'time': time,
371 'get_data': self.get_data,
372 'get_total_amount': self.get_total_amount,
373+ 'get_doc_type': self.get_doc_type,
374 'get_journal_code': self.get_journal_code,
375 })
376
377@@ -45,6 +46,7 @@
378 - an AD defined at line level (first SELECT)
379 - an AD defined at header level (second SELECT)
380 - no AD (third SELECT)
381+ - no account - in that case the AD is seen as empty (fourth SELECT)
382 """
383 self._cr.execute("""SELECT line_number,NULLIF('[' || default_code || '] ' || name_template, '[] ') as product,i.name as description, ac.code || ' ' || ac.name as account, quantity, ROUND(price_unit, 2) as price_unit, ROUND(percentage, 2) as percentage, ROUND(price_subtotal*percentage/100, 2) as sub_total, y.name as currency, n1.code as destination, n2.code as cost_center, n3.code as funding_pool
384 FROM funding_pool_distribution_line a
385@@ -56,7 +58,7 @@
386 INNER JOIN account_account ac ON ac.id = i.account_id
387 LEFT JOIN product_product p ON p.id = i.product_id
388 LEFT JOIN res_currency y ON y.id = s.currency_id
389- WHERE i.invoice_id=%s
390+ WHERE i.invoice_id=%s AND i.account_id IS NOT NULL AND ac.is_analytic_addicted = True
391 UNION ALL
392 SELECT line_number,NULLIF('[' || default_code || '] ' || name_template, '[] ') as product,i.name as description, ac.code || ' ' || ac.name as account, quantity, ROUND(price_unit, 2) as price_unit, ROUND(percentage, 2) as percentage, ROUND(price_subtotal*percentage/100, 2) as sub_total, y.name as currency, n1.code as destination, n2.code as cost_center, n3.code as funding_pool
393 FROM funding_pool_distribution_line a
394@@ -68,7 +70,7 @@
395 INNER JOIN account_analytic_account n3 ON n3.id = a.analytic_id
396 INNER JOIN res_currency y ON y.id = s.currency_id
397 LEFT JOIN product_product p ON p.id = i.product_id
398- WHERE s.id=%s AND ac.is_analytic_addicted = True
399+ WHERE s.id=%s AND i.account_id IS NOT NULL AND ac.is_analytic_addicted = True
400 UNION ALL
401 SELECT line_number, NULLIF('[' || default_code || '] ' || name_template, '[] ') as product, i.name as description,
402 ac.code || ' ' || ac.name as account, quantity, ROUND(price_unit, 2) as price_unit, NULL,
403@@ -78,9 +80,18 @@
404 INNER JOIN account_account ac ON ac.id = i.account_id
405 INNER JOIN res_currency y ON y.id = s.currency_id
406 LEFT JOIN product_product p ON p.id = i.product_id
407- WHERE s.id=%s and is_analytic_addicted = False
408+ WHERE s.id=%s AND i.account_id IS NOT NULL AND is_analytic_addicted = False
409+ UNION ALL
410+ SELECT line_number, NULLIF('[' || default_code || '] ' || name_template, '[] ') as product, i.name as description,
411+ '' as account, quantity, ROUND(price_unit, 2) as price_unit, NULL,
412+ ROUND(price_subtotal, 2) as sub_total, y.name as currency, NULL, NULL, NULL
413+ FROM account_invoice_line i
414+ INNER JOIN account_invoice s ON s.id = i.invoice_id
415+ INNER JOIN res_currency y ON y.id = s.currency_id
416+ LEFT JOIN product_product p ON p.id = i.product_id
417+ WHERE s.id=%s AND i.account_id IS NULL
418 ORDER BY line_number, destination, cost_center, funding_pool
419- """, (invoice_id, invoice_id, invoice_id))
420+ """, (invoice_id, invoice_id, invoice_id, invoice_id))
421 res = self._cr.fetchall()
422 self.total_amount = sum([line[7] or 0.0 for line in res])
423 return res
424@@ -88,11 +99,24 @@
425 def get_total_amount(self):
426 return self.total_amount
427
428+ def get_doc_type(self, inv):
429+ """
430+ Returns the String corresponding to the invoice type.
431+ Note that using "getSel" doesn't work because "doc_type" is a fields.function.
432+ """
433+ inv_doc_type = ""
434+ inv_obj = self.pool.get('account.invoice')
435+ for doc_type in inv_obj._get_invoice_type_list(self.cr, self.uid):
436+ if len(doc_type) >= 2 and inv.doc_type == doc_type[0]:
437+ inv_doc_type = doc_type[1]
438+ break
439+ return inv_doc_type
440+
441 def get_journal_code(self, inv):
442- '''
443+ """
444 If the SI has been (partially or totally) imported in a register, return the Journal Code
445 It the SI has been partially imported in several registers, return : "code1 / code2 / code3"
446- '''
447+ """
448 journal_code_list = []
449 if inv and inv.move_id:
450 absl_ids = self.pool.get('account.bank.statement.line').search(self.cr, self.uid, [('imported_invoice_line_ids', 'in', [x.id for x in inv.move_id.line_id])])
451
452=== modified file 'bin/addons/analytic_distribution/analytic_distribution.py'
453--- bin/addons/analytic_distribution/analytic_distribution.py 2021-04-14 16:45:55 +0000
454+++ bin/addons/analytic_distribution/analytic_distribution.py 2022-01-06 16:20:57 +0000
455@@ -170,7 +170,7 @@
456 account = self.pool.get('account.account').read(cr, uid, account_id, ['destination_ids'])
457 # Check Cost Center lines regarding destination/account and destination/CC links
458 for cc_line in distrib.cost_center_lines:
459- if cc_line.destination_id.id not in account.get('destination_ids', []):
460+ if account and cc_line.destination_id.id not in account.get('destination_ids', []):
461 return 'invalid'
462 if not self.check_dest_cc_compatibility(cr, uid, cc_line.destination_id.id,
463 cc_line.analytic_id and cc_line.analytic_id.id or False, context=context):
464@@ -193,7 +193,7 @@
465 return 'invalid'
466 if doc_date and fp_line.analytic_id and not analytic_acc_obj.is_account_active(fp_line.analytic_id, doc_date):
467 return 'invalid'
468- if fp_line.destination_id.id not in account.get('destination_ids', []):
469+ if account and fp_line.destination_id.id not in account.get('destination_ids', []):
470 return 'invalid'
471 if not self.check_dest_cc_compatibility(cr, uid, fp_line.destination_id.id, fp_line.cost_center_id.id, context=context):
472 return 'invalid'
473
474=== modified file 'bin/addons/analytic_distribution/invoice.py'
475--- bin/addons/analytic_distribution/invoice.py 2020-01-21 16:18:01 +0000
476+++ bin/addons/analytic_distribution/invoice.py 2022-01-06 16:20:57 +0000
477@@ -213,7 +213,7 @@
478 res = {}
479 for invl in self.browse(cr, uid, ids):
480 res[invl.id] = ''
481- if not invl.is_allocatable:
482+ if not invl.is_allocatable or (invl.allow_no_account and not invl.account_id):
483 continue
484 from_header = ''
485 if invl.have_analytic_distribution_from_header:
486
487=== modified file 'bin/addons/msf_profile/i18n/fr_MF.po'
488--- bin/addons/msf_profile/i18n/fr_MF.po 2022-01-05 16:30:54 +0000
489+++ bin/addons/msf_profile/i18n/fr_MF.po 2022-01-06 16:20:57 +0000
490@@ -106168,23 +106168,11 @@
491 msgstr "Champ interne indiquant si le document est lié à un flux \"Supply\""
492
493 #. module: account_override
494-#: code:addons/account_override/account_invoice_sync.py:120
495-#, python-format
496-msgid "Account code %s not found."
497-msgstr "Code comptable %s non trouvé."
498-
499-#. module: account_override
500 #: code:addons/account_override/account_invoice_sync.py:189
501 #, python-format
502 msgid "Impossible to retrieve the currency."
503 msgstr "Impossible de récupérer la devise."
504
505-#. module: account_override
506-#: code:addons/account_override/account_invoice_sync.py:117
507-#, python-format
508-msgid "Impossible to retrieve the account code at line level."
509-msgstr "Impossible de récupérer le code comptable au niveau d'une ligne."
510-
511 #. modules: account_override, account
512 #: code:addons/account_override/account_invoice_sync.py:193
513 #: code:addons/account/wizard/account_invoice_import.py:107
514@@ -106193,12 +106181,6 @@
515 msgstr "Devise %s non trouvée ou inactive."
516
517 #. module: account_override
518-#: code:addons/account_override/account_invoice_sync.py:123
519-#, python-format
520-msgid "Error when retrieving the account at line level."
521-msgstr "Erreur lors de la récupération du compte au niveau d'une ligne."
522-
523-#. module: account_override
524 #: code:addons/account_override/account_invoice_sync.py:235
525 #, python-format
526 msgid "The Intermission counterpart account is missing in the Company form or is inactive."
527@@ -106217,12 +106199,6 @@
528 msgstr "Impossible de récupérer la description de la ligne."
529
530 #. module: account_override
531-#: code:addons/account_override/account_invoice_sync.py:128
532-#, python-format
533-msgid "The account \"%s - %s\" is inactive."
534-msgstr "Le compte \"%s - %s\" est inactif."
535-
536-#. module: account_override
537 #: code:addons/account_override/account_invoice_sync.py:135
538 #, python-format
539 msgid "Unit of Measure %s not found."
540@@ -113224,6 +113200,28 @@
541 msgid "You must select an old version of the Kit to be able to import from it"
542 msgstr "Vous devez sélectionner une ancienne version de ce Kit pour pouvoir importer depuis ce Kit"
543
544+#. module: account_override
545+#: field:account.invoice.line,allow_no_account:0
546+msgid "Allow an empty account on the line"
547+msgstr "Autoriser un compte vide sur la ligne"
548+
549+#. module: account_override
550+#: sql_constraint:account.invoice.line:0
551+msgid "The invoice lines must have an account."
552+msgstr "Les lignes de facture doivent avoir un compte."
553+
554+#. module: register_accounting
555+#: code:addons/register_accounting/invoice.py:369
556+#, python-format
557+msgid "The account is missing on the line No. %s."
558+msgstr "Il manque le compte sur la ligne N°%s."
559+
560+#. module: account_override
561+#: code:addons/account_override/invoice.py:1759
562+#, python-format
563+msgid "Duplication not allowed. Please set an account on all lines first."
564+msgstr "Duplication non autorisée. Veuillez d'abord définir un compte sur toutes les lignes."
565+
566 #. module: analytic_override
567 #: field:account.analytic.line,ad_updated:0
568 msgid "Analytic Distribution Updated"
569
570=== modified file 'bin/addons/register_accounting/account_invoice_view.xml'
571--- bin/addons/register_accounting/account_invoice_view.xml 2021-10-21 09:49:00 +0000
572+++ bin/addons/register_accounting/account_invoice_view.xml 2022-01-06 16:20:57 +0000
573@@ -49,7 +49,10 @@
574 colors="red:analytic_distribution_state in ('invalid', 'invalid_small_amount') or inactive_product == True;black:inactive_product == False and analytic_distribution_state in ('none','valid')">
575 <field name="name"/>
576 <field name="reference"/>
577- <field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]" name="account_id"/>
578+ <field name="account_id"
579+ domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]"
580+ required="1"
581+ />
582 <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-stock_symbol-selection"
583 context="{'d_journal_id': parent.journal_id , 'd_partner_id': parent.partner_id, 'd_address_invoice_id':parent.address_invoice_id,'d_date_invoice': parent.date_invoice, 'd_register_posting_date': parent.register_posting_date, 'd_document_date': parent.document_date, 'd_account_id': parent.account_id, 'd_partner_bank_id': parent.partner_bank_id, 'd_payment_term': parent.payment_term, 'd_name': parent.name, 'd_origine': parent.origin, 'd_address_contact_id': parent.address_contact_id, 'd_user_id': parent.user_id, 'd_comment': parent.comment,'d_reference': parent.reference}"
584 />
585@@ -176,7 +179,10 @@
586 <button name="button_open_analytic_lines" string="Have been corrected" type="object" icon="terp-mail-" attrs="{'invisible': [('is_corrected', '=', False)]}"/>
587 <field name="name"/>
588 <field name="reference" />
589- <field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]" name="account_id"/>
590+ <field name="account_id"
591+ domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]"
592+ required="1"
593+ />
594 <field name="analytic_distribution_state_recap" attrs="{'invisible': [('is_allocatable', '=', False)]}"/>
595 <field name="analytic_distribution_state" invisible="1"/>
596 <field name="have_analytic_distribution_from_header" invisible="1"/>
597
598=== modified file 'bin/addons/register_accounting/invoice.py'
599--- bin/addons/register_accounting/invoice.py 2017-08-18 14:07:26 +0000
600+++ bin/addons/register_accounting/invoice.py 2022-01-06 16:20:57 +0000
601@@ -358,6 +358,20 @@
602 aal_obj.write(cr, uid, aal_ids, {'reference': reference}, context=context)
603 return True
604
605+ def _check_empty_account(self, cr, uid, ids, context=None):
606+ """
607+ Raises an error if one of the invoice lines has no account.
608+ """
609+ if context is None:
610+ context = {}
611+ if isinstance(ids, (int, long)):
612+ ids = [ids]
613+ for inv in self.browse(cr, uid, ids, fields_to_fetch=['invoice_line'], context=context):
614+ for invl in inv.invoice_line:
615+ if not invl.account_id:
616+ raise osv.except_osv(_('Error'), _('The account is missing on the line No. %s.') % invl.line_number)
617+ return True
618+
619 def action_open_invoice(self, cr, uid, ids, context=None, *args):
620 """
621 Add down payment check after others verifications
622@@ -368,6 +382,7 @@
623 if isinstance(ids, (int, long)):
624 ids = [ids]
625 # Browse invoice and all invoice lines to detect a non-valid line
626+ self._check_empty_account(cr, uid, ids, context=context)
627 self._check_analytic_distribution_state(cr, uid, ids)
628 # Default behaviour
629 res = super(account_invoice, self).action_open_invoice(cr, uid, ids, context)

Subscribers

People subscribed via source and target branches