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

Proposed by jftempo
Status: Merged
Merged at revision: 5440
Proposed branch: lp:~julie-w/unifield-server/US-6076
Merge into: lp:unifield-server
Diff against target: 2105 lines (+1121/-125)
22 files modified
bin/addons/account/account_invoice_view.xml (+99/-35)
bin/addons/account/invoice.py (+55/-13)
bin/addons/account/wizard/account_invoice_refund.py (+36/-7)
bin/addons/account/wizard/account_invoice_refund_view.xml (+4/-1)
bin/addons/account_override/__init__.py (+1/-0)
bin/addons/account_override/account_invoice_sync.py (+362/-0)
bin/addons/account_override/account_invoice_view.xml (+119/-24)
bin/addons/account_override/invoice.py (+103/-11)
bin/addons/analytic_distribution/account_invoice_refund.py (+26/-5)
bin/addons/analytic_distribution/account_invoice_view.xml (+8/-2)
bin/addons/msf_audittrail/audittrail_invoice_data.yml (+2/-1)
bin/addons/msf_outgoing/msf_outgoing.py (+2/-0)
bin/addons/msf_profile/data/patches.xml (+5/-0)
bin/addons/msf_profile/i18n/fr_MF.po (+244/-15)
bin/addons/msf_profile/msf_profile.py (+15/-0)
bin/addons/msf_sync_data_server/data/sync_server.message_rule.csv (+4/-0)
bin/addons/purchase/purchase_workflow.py (+3/-3)
bin/addons/register_accounting/account_invoice_view.xml (+14/-2)
bin/addons/sale/sale_workflow.py (+3/-3)
bin/addons/stock/stock.py (+8/-0)
bin/addons/stock_override/stock.py (+5/-0)
bin/addons/sync_so/picking.py (+3/-3)
To merge this branch: bzr merge lp:~julie-w/unifield-server/US-6076
Reviewer Review Type Date Requested Status
UniField Reviewer Team Pending
Review via email: mp+370103@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 2018-02-13 15:48:51 +0000
3+++ bin/addons/account/account_invoice_view.xml 2019-07-15 07:18:58 +0000
4@@ -53,11 +53,29 @@
5 <form string="Invoice Line">
6 <notebook>
7 <page string="Line">
8- <field name="product_id" on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.address_invoice_id, parent.currency_id, {'company_id': parent.company_id})" default_focus="1"/>
9- <field name="uos_id" on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.address_invoice_id, parent.currency_id, {'company_id': parent.company_id})"/>
10- <field name="quantity"/>
11- <field name="price_unit"/>
12- <field name="discount" groups="base.group_extended"/>
13+ <field name="invoice_type" invisible="1"/>
14+ <field name="from_supply" invisible="1"/>
15+ <field name="partner_type" invisible="1"/>
16+ <field name="product_id"
17+ on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.address_invoice_id, parent.currency_id, {'company_id': parent.company_id})"
18+ default_focus="1"
19+ attrs="{'readonly': ['|',
20+ '&amp;', ('invoice_type', '=', 'in_invoice'), ('synced', '=', True),
21+ '&amp;', '&amp;', ('invoice_type', '=', 'out_invoice'), ('from_supply', '=', True), ('partner_type', 'in', ('intermission', 'section'))]}"
22+ />
23+ <field name="uos_id"
24+ on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.address_invoice_id, parent.currency_id, {'company_id': parent.company_id})"
25+ attrs="{'readonly': ['|',
26+ '&amp;', ('invoice_type', '=', 'in_invoice'), ('synced', '=', True),
27+ '&amp;', '&amp;', ('invoice_type', '=', 'out_invoice'), ('from_supply', '=', True), ('partner_type', 'in', ('intermission', 'section'))]}"
28+ />
29+ <field name="quantity" attrs="{'readonly': ['|',
30+ '&amp;', ('invoice_type', '=', 'in_invoice'), ('synced', '=', True),
31+ '&amp;', '&amp;', ('invoice_type', '=', 'out_invoice'), ('from_supply', '=', True), ('partner_type', 'in', ('intermission', 'section'))]}"
32+ />
33+ <field name="price_unit" attrs="{'readonly': [('invoice_type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
34+ <field name="discount" groups="base.group_extended"
35+ attrs="{'readonly': [('invoice_type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
36 <field colspan="4" name="name"/>
37 <field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view')]" name="account_id"/>
38 <field domain="[('type','&lt;&gt;','view'), ('company_id', '=', parent.company_id), ('parent_id', '!=', False)]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
39@@ -152,10 +170,17 @@
40 <field name="journal_id" widget="selection"/>
41 <field name="number" readonly="1"/>
42 <field name="type" invisible="1"/>
43- <field name="currency_id" width="50"/>
44- <button name="%(action_account_change_currency)d" type="action" icon="terp-stock_effects-object-colorize" string="Change" attrs="{'invisible':[('state','!=','draft')]}" groups="account.group_account_user"/>
45+ <field name="currency_id" width="50" attrs="{'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
46+ <button name="%(action_account_change_currency)d" type="action" icon="terp-stock_effects-object-colorize"
47+ string="Change" groups="account.group_account_user"
48+ attrs="{'invisible':[('state', '!=', 'draft')],
49+ 'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
50 <newline/>
51- <field string="Supplier" name="partner_id" domain="[('supplier', '=', True)]" on_change="onchange_partner_id(type,partner_id,date_invoice,payment_term, partner_bank_id,company_id)" context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}"/>
52+ <field string="Supplier" name="partner_id" domain="[('supplier', '=', True)]"
53+ on_change="onchange_partner_id(type, partner_id, date_invoice, payment_term, partner_bank_id, company_id)"
54+ context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}"
55+ attrs="{'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}"
56+ />
57 <field domain="[('partner_id','=',partner_id)]" name="address_invoice_id"/>
58 <field name="fiscal_position" groups="base.group_extended" widget="selection"/>
59 <newline/>
60@@ -187,25 +212,39 @@
61 </tree>
62 </field>
63 <group col="1" colspan="2">
64- <field name="tax_line" nolabel="1">
65- <tree editable="bottom" string="Taxes">
66- <field name="invoice_id" invisible="True"/>
67- <field name="account_tax_id" on_change="tax_code_change(account_tax_id,parent.amount_untaxed,context)"/>
68- <field name="name"/>
69-
70- <field name="account_id" groups="account.group_account_invoice"
71- domain="[('type', 'not in', ['view', 'consolidation']),
72- ('user_type_code', '=', 'tax')]"/>
73-
74- <field name="base" on_change="base_change(base,parent.currency_id,parent.company_id,parent.date_invoice)" readonly="1"/>
75- <field name="amount" on_change="amount_change(amount,parent.currency_id,parent.company_id,parent.date_invoice)"/>
76-
77- <field invisible="True" name="base_amount"/>
78- <field invisible="True" name="tax_amount"/>
79- <field name="factor_base" invisible="True"/>
80- <field name="factor_tax" invisible="True"/>
81- </tree>
82- </field>
83+ <group colspan="1" attrs="{'invisible': ['|', ('type', '!=', 'in_invoice'), ('synced', '=', False)]}">
84+ <!-- SI synced: Tax Lines always in read-only -->
85+ <field name="tax_line_ids" nolabel="1">
86+ <tree noteditable="1" string="Taxes">
87+ <field name="account_tax_id"/>
88+ <field name="name"/>
89+ <field name="account_id"/>
90+ <field name="base"/>
91+ <field name="amount"/>
92+ </tree>
93+ </field>
94+ </group>
95+ <group colspan="1" attrs="{'invisible': [('type', '=', 'in_invoice'), ('synced', '=', True)]}">
96+ <field name="tax_line" nolabel="1">
97+ <tree editable="bottom" string="Taxes">
98+ <field name="invoice_id" invisible="True"/>
99+ <field name="account_tax_id" on_change="tax_code_change(account_tax_id,parent.amount_untaxed,context)"/>
100+ <field name="name"/>
101+
102+ <field name="account_id" groups="account.group_account_invoice"
103+ domain="[('type', 'not in', ['view', 'consolidation']),
104+ ('user_type_code', '=', 'tax')]"/>
105+
106+ <field name="base" on_change="base_change(base,parent.currency_id,parent.company_id,parent.date_invoice)" readonly="1"/>
107+ <field name="amount" on_change="amount_change(amount,parent.currency_id,parent.company_id,parent.date_invoice)"/>
108+
109+ <field invisible="True" name="base_amount"/>
110+ <field invisible="True" name="tax_amount"/>
111+ <field name="factor_base" invisible="True"/>
112+ <field name="factor_tax" invisible="True"/>
113+ </tree>
114+ </field>
115+ </group>
116 </group>
117 <group col="4" colspan="2">
118 <button colspan="2" name="button_reset_taxes" states="draft" string="Compute Taxes" type="object" icon="terp-stock_format-scientific"/>
119@@ -230,11 +269,14 @@
120 <field name="company_id" on_change="onchange_company_id(company_id,partner_id,type,invoice_line,currency_id)" widget="selection" groups="base.group_multi_company"/>
121 <newline/>
122 <field name="payment_term" widget="selection"/>
123- <field name="name"/>
124+ <field name="name"
125+ attrs="{'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True), ('from_supply', '=', True)]}"/>
126 <newline/>
127- <field name="origin" groups="base.group_extended"/>
128+ <field name="origin" groups="base.group_extended"
129+ attrs="{'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True), ('from_supply', '=', True)]}"/>
130 <field domain="[('partner_id','=',partner_id)]" name="address_contact_id" groups="base.group_extended"/>
131- <field name="user_id"/>
132+ <field name="user_id"
133+ attrs="{'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
134 <field name="move_id" groups="account.group_account_user"/>
135 <separator colspan="4" string="Additional Information"/>
136 <field colspan="4" name="comment" nolabel="1"/>
137@@ -273,7 +315,13 @@
138 <field name="currency_id" width="50"/>
139 <button name="%(action_account_change_currency)d" type="action" icon="terp-stock_effects-object-colorize" string="Change" attrs="{'invisible':[('state','!=','draft')]}" groups="account.group_account_user"/>
140 <newline/>
141- <field string="Customer" name="partner_id" domain="[('customer', '=', True)]" on_change="onchange_partner_id(type,partner_id,date_invoice,payment_term, partner_bank_id,company_id)" groups="base.group_user" context="{'search_default_customer': 1}"/>
142+ <field string="Customer" name="partner_id"
143+ domain="[('customer', '=', True)]"
144+ on_change="onchange_partner_id(type,partner_id,date_invoice,payment_term, partner_bank_id,company_id)"
145+ groups="base.group_user" context="{'search_default_customer': 1}"
146+ attrs="{'readonly': [('type', '=', 'out_invoice'),
147+ ('from_supply', '=', True),
148+ ('partner_type', 'in', ('intermission', 'section'))]}"/>
149 <field domain="[('partner_id','=',partner_id)]" name="address_invoice_id"/>
150 <field name="fiscal_position" groups="base.group_extended" widget="selection"/>
151 <newline/>
152@@ -286,9 +334,11 @@
153 <notebook colspan="4">
154 <page string="Invoice">
155 <field domain="[('company_id', '=', company_id),('type','=', 'receivable')]" name="account_id" groups="account.group_account_user"/>
156- <field name="name"/>
157+ <field name="name" attrs="{'readonly': [('type', '=', 'out_invoice'),
158+ ('from_supply', '=', True),
159+ ('partner_type', 'in', ('intermission', 'section'))]}"/>
160 <field name="payment_term" widget="selection"/>
161- <field colspan="4" name="invoice_line" nolabel="1" widget="one2many_list" context="{'fake':1}"/>
162+ <field colspan="4" name="invoice_line" nolabel="1" widget="one2many_list" context="{'fake': 1, 'from_inv_form': True}"/>
163 <group col="1" colspan="2">
164 <field name="tax_line" nolabel="1">
165 <tree editable="bottom" string="Taxes">
166@@ -328,11 +378,15 @@
167 <field name="company_id" on_change="onchange_company_id(company_id,partner_id,type,invoice_line,currency_id)" widget="selection" groups="base.group_multi_company"/>
168 <newline/>
169 <field name="date_due"/>
170- <field name="user_id"/>
171+ <field name="user_id" attrs="{'readonly': [('type', '=', 'out_invoice'),
172+ ('from_supply', '=', True),
173+ ('partner_type', 'in', ('intermission', 'section'))]}"/>
174 <newline/>
175 <field domain="[('partner_id.ref_companies', 'in', [company_id])]" name="partner_bank_id"
176 groups="base.group_extended"/>
177- <field name="origin"/>
178+ <field name="origin" attrs="{'readonly': [('type', '=', 'out_invoice'),
179+ ('from_supply', '=', True),
180+ ('partner_type', 'in', ('intermission', 'section'))]}"/>
181 <field colspan="4" domain="[('partner_id','=',partner_id)]" name="address_contact_id"
182 groups="base.group_extended"/>
183 <field name="move_id" groups="account.group_account_user"/>
184@@ -353,6 +407,16 @@
185 </tree>
186 </field>
187 </page>
188+ <!-- display the Counterpart Invoice tab in STV, hide it in Customer Refunds -->
189+ <page string="Counterpart Invoice" attrs="{'invisible': [('type', '!=', 'out_invoice')]}">
190+ <field name="from_supply" invisible="1"/>
191+ <field name="synced"
192+ attrs="{'readonly': ['|', ('from_supply', '=', True), ('state', '!=', 'draft')]}"
193+ on_change="onchange_synced(synced, partner_id)"/>
194+ <newline/>
195+ <field name="counterpart_inv_number"/>
196+ <field name="counterpart_inv_status"/>
197+ </page>
198 </notebook>
199 </form>
200 </field>
201
202=== modified file 'bin/addons/account/invoice.py'
203--- bin/addons/account/invoice.py 2019-06-05 09:09:37 +0000
204+++ bin/addons/account/invoice.py 2019-07-15 07:18:58 +0000
205@@ -26,6 +26,7 @@
206 import netsvc
207 from osv import fields, osv, orm
208 from tools.translate import _
209+from msf_partner import PARTNER_TYPE
210
211
212 class account_invoice(osv.osv):
213@@ -257,7 +258,7 @@
214 _order = "id desc"
215
216 _columns = {
217- 'name': fields.char('Description', size=64, select=True, readonly=True, states={'draft':[('readonly',False)]}),
218+ 'name': fields.char('Description', size=256, select=True, readonly=True, states={'draft': [('readonly', False)]}),
219 'origin': fields.char('Source Document', size=512, help="Reference of the document that produced this invoice.", readonly=True, states={'draft':[('readonly',False)]}),
220 'type': fields.selection([
221 ('out_invoice','Customer Invoice'),
222@@ -302,7 +303,8 @@
223 'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."),
224 'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}),
225 'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}),
226-
227+ 'tax_line_ids': fields.related('tax_line', type='one2many', relation='account.invoice.tax', string='Tax Lines',
228+ readonly=True, store=False, help="Internal field for taxes always in read-only mode"),
229 'move_id': fields.many2one('account.move', 'Journal Entry', readonly=True, select=1, ondelete='restrict', help="Link to the automatically generated Journal Items."),
230 'amount_untaxed': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Untaxed',
231 store={
232@@ -463,13 +465,17 @@
233 def unlink(self, cr, uid, ids, context=None):
234 if context is None:
235 context = {}
236- invoices = self.read(cr, uid, ids, ['state'], context=context)
237+ invoices = self.read(cr, uid, ids, ['state', 'synced', 'from_supply'], context=context)
238 unlink_ids = []
239 for t in invoices:
240- if t['state'] in ('draft', 'cancel'):
241+ if t['state'] not in ('draft', 'cancel'):
242+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) that are already opened or paid !'))
243+ elif t['from_supply']:
244+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) generated by a Supply workflow!'))
245+ elif t['synced']:
246+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) set as "Synchronized"!'))
247+ else:
248 unlink_ids.append(t['id'])
249- else:
250- raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) that are already opened or paid !'))
251 osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
252 return True
253
254@@ -481,6 +487,7 @@
255 acc_id = False
256 bank_id = False
257 fiscal_position = False
258+ partner_type = False
259
260 opt = [('uid', str(uid))]
261 if partner_id:
262@@ -490,6 +497,8 @@
263 contact_addr_id = res['contact']
264 invoice_addr_id = res['invoice']
265 p = self.pool.get('res.partner').browse(cr, uid, partner_id)
266+ partner_type = p.partner_type # update the partner type immediately as it is used in a domain in attrs
267+
268 if company_id:
269 if p.property_account_receivable.company_id.id != company_id and p.property_account_payable.company_id.id != company_id:
270 property_obj = self.pool.get('ir.property')
271@@ -526,7 +535,8 @@
272 'address_invoice_id': invoice_addr_id,
273 'account_id': acc_id,
274 'payment_term': partner_payment_term,
275- 'fiscal_position': fiscal_position
276+ 'fiscal_position': fiscal_position,
277+ 'partner_type': partner_type,
278 }
279 }
280
281@@ -662,6 +672,22 @@
282 val['currency_id'] = currency.id
283 return {'value': val, 'domain': dom}
284
285+ def onchange_synced(self, cr, uid, ids, synced, partner_id):
286+ """
287+ Resets "synced" field and informs the user in case the box is ticked whereas the partner is neither Intermission nor Intersection
288+ """
289+ res = {}
290+ partner_obj = self.pool.get('res.partner')
291+ if synced and partner_id:
292+ if partner_obj.browse(cr, uid, partner_id, fields_to_fetch=['partner_type']).partner_type not in ('intermission', 'section'):
293+ warning = {
294+ 'title': _('Warning!'),
295+ 'message': _('Synchronization is allowed only with Intermission and Intersection partners.')
296+ }
297+ res['warning'] = warning
298+ res['value'] = {'synced': False, }
299+ return res
300+
301 # go from canceled state to draft state
302 def action_cancel_draft(self, cr, uid, ids, *args):
303 self.write(cr, uid, ids, {'state':'draft'})
304@@ -705,6 +731,10 @@
305 'move_name':False,
306 'internal_number': False,
307 'main_purchase_id': False,
308+ 'from_supply': False,
309+ 'synced': False,
310+ 'counterpart_inv_number': False,
311+ 'counterpart_inv_status': False,
312 })
313 if 'date_invoice' not in default:
314 default.update({
315@@ -1324,12 +1354,18 @@
316 for invoice in invoices:
317 del invoice['id']
318
319- type_dict = {
320- 'out_invoice': 'out_refund', # Customer Invoice
321- 'in_invoice': 'in_refund', # Supplier Invoice
322- 'out_refund': 'out_invoice', # Customer Refund
323- 'in_refund': 'in_invoice', # Supplier Refund
324- }
325+ if context.get('is_intermission', False):
326+ type_dict = {
327+ 'out_invoice': 'in_invoice', # IVO
328+ 'in_invoice': 'out_invoice', # IVI
329+ }
330+ else:
331+ type_dict = {
332+ 'out_invoice': 'out_refund', # Customer Invoice
333+ 'in_invoice': 'in_refund', # Supplier Invoice
334+ 'out_refund': 'out_invoice', # Customer Refund
335+ 'in_refund': 'in_invoice', # Supplier Refund
336+ }
337
338 invoice_lines = obj_invoice_line.read(cr, uid, invoice['invoice_line'])
339 invoice_lines = self._refund_cleanup_lines(cr, uid, invoice_lines, is_account_inv_line=True, context=context)
340@@ -1355,6 +1391,10 @@
341 'journal_id': refund_journal_ids,
342 'origin': invoice['number']
343 })
344+ if context.get('is_intermission', False):
345+ invoice.update({
346+ 'is_intermission': True,
347+ })
348 if period_id:
349 invoice.update({
350 'period_id': period_id,
351@@ -1515,6 +1555,8 @@
352 'name': fields.char('Description', size=256, required=True),
353 'origin': fields.char('Origin', size=512, help="Reference of the document that produced this invoice."),
354 'invoice_id': fields.many2one('account.invoice', 'Invoice Reference', ondelete='cascade', select=True),
355+ 'partner_type': fields.related('invoice_id', 'partner_type', string='Partner Type', type='selection',
356+ selection=PARTNER_TYPE, readonly=True, store=False),
357 'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null'),
358 'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
359 '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."),
360
361=== modified file 'bin/addons/account/wizard/account_invoice_refund.py'
362--- bin/addons/account/wizard/account_invoice_refund.py 2018-05-14 12:41:04 +0000
363+++ bin/addons/account/wizard/account_invoice_refund.py 2019-07-15 07:18:58 +0000
364@@ -29,13 +29,24 @@
365
366 _name = "account.invoice.refund"
367 _description = "Invoice Refund"
368+
369+ def _get_filter_refund(self, cr, uid, context=None):
370+ """
371+ Returns the selectable Refund Types (no simple "Refund" in case of an IVO/IVI)
372+ """
373+ if context is None:
374+ context = {}
375+ if context.get('is_intermission', False):
376+ return [('modify', 'Modify'), ('cancel', 'Cancel')]
377+ return [('modify', 'Modify'), ('refund', 'Refund'), ('cancel', 'Cancel')]
378+
379 _columns = {
380 'date': fields.date('Operation date', help='This date will be used as the invoice date for Refund Invoice and Period will be chosen accordingly!'),
381 'period': fields.many2one('account.period', 'Force period'),
382 'journal_id': fields.many2one('account.journal', 'Refund Journal', hide_default_menu=True,
383 help='You can select here the journal to use for the refund invoice that will be created. If you leave that field empty, it will use the same journal as the current invoice.'),
384 'description': fields.char('Description', size=128, required=True),
385- 'filter_refund': fields.selection([('modify', 'Modify'), ('refund', 'Refund'), ('cancel', 'Cancel')], "Refund Type", required=True, help='Refund invoice base on this type. You can not Modify and Cancel if the invoice is already reconciled'),
386+ 'filter_refund': fields.selection(_get_filter_refund, "Refund Type", required=True, help='Refund invoice based on this type. You can not Modify and Cancel if the invoice is already reconciled'),
387 }
388
389 def _get_journal(self, cr, uid, context=None):
390@@ -145,7 +156,10 @@
391 if inv.state in ['draft', 'proforma2', 'cancel']:
392 raise osv.except_osv(_('Error !'), _('Can not %s draft/proforma/cancel invoice.') % (mode))
393 if mode in ('cancel', 'modify') and inv_obj.has_one_line_reconciled(cr, uid, [inv.id], context=context):
394- raise osv.except_osv(_('Error !'), _('Can not %s invoice which is already reconciled, invoice should be unreconciled first. You can only Refund this invoice') % (mode))
395+ if inv.is_intermission:
396+ # error specific to IVO/IVI for which there is no simple refund option
397+ raise osv.except_osv(_('Error !'), _('Cannot %s an Intermission Voucher which is already reconciled, it should be unreconciled first.') % _(mode))
398+ raise osv.except_osv(_('Error !'), _('Can not %s invoice which is already reconciled, invoice should be unreconciled first. You can only Refund this invoice') % _(mode))
399 if form['period']:
400 period = form['period']
401 else:
402@@ -188,8 +202,14 @@
403 refund_id = self._hook_create_refund(cr, uid, [inv.id], date, period, description, journal_id, form, context=context)
404 del context['refund_mode'] # ignore it for the remaining process (in particular for the SI created in a refund modify...)
405 refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
406+ # for Intermission Vouchers OUT: at standard creation time there is no "check_total" entered manually,
407+ # its value is always 0.0 => use the "amount_total" value for the IVI generated so it won't block at validation step
408+ if inv.is_intermission and inv.type == 'out_invoice':
409+ check_total = inv.amount_total or 0.0
410+ else:
411+ check_total = inv.check_total
412 inv_obj.write(cr, uid, [refund.id], {'date_due': date,
413- 'check_total': inv.check_total})
414+ 'check_total': check_total})
415
416 created_inv.append(refund_id[0])
417 if mode in ('cancel', 'modify'):
418@@ -230,6 +250,7 @@
419 'period_id': False,
420 'name': description,
421 'origin': source_doc,
422+ 'is_intermission': inv.is_intermission,
423 })
424 for field in self._hook_fields_m2o_for_modify_refund(cr, uid):
425 invoice[field] = invoice[field] and invoice[field][0]
426@@ -254,11 +275,19 @@
427 # write on JIs without recreating AJIs
428 account_m_line_obj.write(cr, uid, ji_ids, {'is_si_refund': True}, context=context, check=False, update_check=False)
429
430- if inv.type in ('out_invoice', 'out_refund'):
431- xml_id = 'action_invoice_tree3'
432+ if context.get('is_intermission', False):
433+ module = 'account_override'
434+ if inv.type == 'in_invoice':
435+ xml_id = 'action_intermission_out'
436+ else:
437+ xml_id = 'action_intermission_in'
438 else:
439- xml_id = 'action_invoice_tree4'
440- result = mod_obj.get_object_reference(cr, uid, 'account', xml_id)
441+ module = 'account'
442+ if inv.type in ('out_invoice', 'out_refund'):
443+ xml_id = 'action_invoice_tree3'
444+ else:
445+ xml_id = 'action_invoice_tree4'
446+ result = mod_obj.get_object_reference(cr, uid, module, xml_id)
447 id = result and result[1] or False
448 result = act_obj.read(cr, uid, id, context=context)
449 invoice_domain = eval(result['domain'])
450
451=== modified file 'bin/addons/account/wizard/account_invoice_refund_view.xml'
452--- bin/addons/account/wizard/account_invoice_refund_view.xml 2018-05-15 08:55:26 +0000
453+++ bin/addons/account/wizard/account_invoice_refund_view.xml 2019-07-15 07:18:58 +0000
454@@ -15,11 +15,14 @@
455 <field name="date" required="1"/>
456 <field name="period"/>
457 <field name="filter_refund"/>
458+ <field name="is_intermission" invisible="1"/>
459 </group>
460 <separator colspan="4"/>
461 <group col="4" colspan="4" fill="1">
462 <label align="0.0" width="550" colspan="4" string="Modify Invoice: Cancels the current invoice and creates a new copy of it ready for editing."/>
463- <label align="0.0" width="300" string="Refund Invoice: Creates the refund invoice, ready for editing."/>
464+ <group attrs="{'invisible': [('is_intermission', '=', True)] }">
465+ <label align="0.0" width="300" string="Refund Invoice: Creates the refund invoice, ready for editing."/>
466+ </group>
467 <label align="0.0" width="500" colspan="4" string="Cancel Invoice: Creates the refund invoice, validate and reconcile it to cancel the current invoice."/>
468 </group>
469 <separator colspan="4"/>
470
471=== modified file 'bin/addons/account_override/__init__.py'
472--- bin/addons/account_override/__init__.py 2019-01-08 11:20:04 +0000
473+++ bin/addons/account_override/__init__.py 2019-07-15 07:18:58 +0000
474@@ -230,4 +230,5 @@
475 import report
476 import wizard
477 import finance_export
478+import account_invoice_sync
479 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
480
481=== added file 'bin/addons/account_override/account_invoice_sync.py'
482--- bin/addons/account_override/account_invoice_sync.py 1970-01-01 00:00:00 +0000
483+++ bin/addons/account_override/account_invoice_sync.py 2019-07-15 07:18:58 +0000
484@@ -0,0 +1,362 @@
485+#!/usr/bin/env python
486+#-*- encoding:utf-8 -*-
487+##############################################################################
488+#
489+# OpenERP, Open Source Management Solution
490+# Copyright (C) 2019 TeMPO Consulting, MSF. All Rights Reserved
491+#
492+# This program is free software: you can redistribute it and/or modify
493+# it under the terms of the GNU Affero General Public License as
494+# published by the Free Software Foundation, either version 3 of the
495+# License, or (at your option) any later version.
496+#
497+# This program is distributed in the hope that it will be useful,
498+# but WITHOUT ANY WARRANTY; without even the implied warranty of
499+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
500+# GNU Affero General Public License for more details.
501+#
502+# You should have received a copy of the GNU Affero General Public License
503+# along with this program. If not, see <http://www.gnu.org/licenses/>.
504+#
505+##############################################################################
506+
507+
508+from osv import osv
509+from osv import fields
510+from tools.translate import _
511+import logging
512+import time
513+
514+
515+class account_invoice_sync(osv.osv):
516+ _inherit = 'account.invoice'
517+ _logger = logging.getLogger('------sync.account.invoice')
518+
519+ _columns = {
520+ 'synced': fields.boolean("Synchronized"),
521+ 'from_supply': fields.boolean('From Supply', help="Internal field indicating whether the document is related to a Supply workflow"),
522+ 'counterpart_inv_number': fields.char('Counterpart Invoice Number', size=64, readonly=True),
523+ 'counterpart_inv_status': fields.selection([
524+ ('draft', 'Draft'),
525+ ('open', 'Open'),
526+ ('paid', 'Paid'),
527+ ('cancel', 'Cancelled'),
528+ ], string='Counterpart Invoice Status', readonly=True),
529+ }
530+
531+ _defaults = {
532+ 'synced': lambda *a: False,
533+ 'from_supply': lambda *a: False,
534+ }
535+
536+ def _create_analytic_distrib(self, cr, uid, vals, distrib, context=None):
537+ """
538+ Updates vals with the new analytic_distribution_id created based on the distrib in parameter if it exists
539+ """
540+ if context is None:
541+ context = {}
542+ analytic_distrib_obj = self.pool.get('analytic.distribution')
543+ cc_distrib_line_obj = self.pool.get('cost.center.distribution.line')
544+ fp_distrib_line_obj = self.pool.get('funding.pool.distribution.line')
545+ data_obj = self.pool.get('ir.model.data')
546+ # get the Funding Pool "PF"
547+ try:
548+ fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
549+ except ValueError:
550+ fp_id = 0
551+ if distrib: # original distrib from PO or PO line
552+ # create the Analytic Distribution
553+ distrib_id = analytic_distrib_obj.create(cr, uid, {}, context=context)
554+ for cc_line in distrib.cost_center_lines:
555+ distrib_vals = {
556+ 'analytic_id': cc_line.analytic_id and cc_line.analytic_id.id, # analytic_id = Cost Center for the CC distrib line
557+ 'percentage': cc_line.percentage or 0.0,
558+ 'distribution_id': distrib_id,
559+ 'currency_id': cc_line.currency_id.id,
560+ 'destination_id': cc_line.destination_id.id,
561+ }
562+ cc_distrib_line_obj.create(cr, uid, distrib_vals, context=context)
563+ distrib_vals.update({
564+ 'analytic_id': fp_id, # analytic_id = Funding Pool for the FP distrib line
565+ 'cost_center_id': cc_line.analytic_id and cc_line.analytic_id.id,
566+ })
567+ fp_distrib_line_obj.create(cr, uid, distrib_vals, context=context)
568+ vals.update({'analytic_distribution_id': distrib_id,})
569+
570+ def _create_invoice_lines(self, cr, uid, inv_lines_data, inv_id, inv_posting_date, inv_linked_po, from_supply, context=None):
571+ """
572+ Creates the lines of the automatic counterpart invoice (inv_id) generated at synchro time.
573+ """
574+ if context is None:
575+ context = {}
576+ so_po_common_obj = self.pool.get('so.po.common')
577+ product_obj = self.pool.get('product.product')
578+ account_obj = self.pool.get('account.account')
579+ product_uom_obj = self.pool.get('product.uom')
580+ inv_line_obj = self.pool.get('account.invoice.line')
581+ for inv_line in inv_lines_data:
582+ line_name = inv_line.get('name', '')
583+ if not line_name: # required field
584+ raise osv.except_osv(_('Error'), _("Impossible to retrieve the line description."))
585+ product_id = False
586+ product_data = inv_line.get('product_id', {})
587+ line_account_id = False
588+ # for the lines related to a product: use the account of the product / else use the one of the source invoice line
589+ if product_data:
590+ default_code = product_data.get('default_code', '')
591+ product_id = so_po_common_obj.get_product_id(cr, uid, product_data, default_code=default_code, context=context) or False
592+ if not product_id:
593+ raise osv.except_osv(_('Error'), _("Product %s not found.") % default_code)
594+ product = product_obj.browse(cr, uid, product_id, fields_to_fetch=['active', 'default_code', 'product_tmpl_id', 'categ_id'],
595+ context=context)
596+ if not product.active:
597+ raise osv.except_osv(_('Error'), _("The product %s is inactive.") % product.default_code or '')
598+ line_account_id = product.product_tmpl_id.property_account_expense and product.product_tmpl_id.property_account_expense.id
599+ if not line_account_id:
600+ line_account_id = product.categ_id and product.categ_id.property_account_expense_categ and product.categ_id.property_account_expense_categ.id
601+ else:
602+ account_code = inv_line.get('account_id', {}).get('code', '')
603+ if not account_code:
604+ raise osv.except_osv(_('Error'), _("Impossible to retrieve the account code at line level."))
605+ account_ids = account_obj.search(cr, uid, [('code', '=', account_code)], limit=1, context=context)
606+ if not account_ids:
607+ raise osv.except_osv(_('Error'), _("Account code %s not found.") % account_code)
608+ line_account_id = account_ids[0]
609+ if not line_account_id:
610+ raise osv.except_osv(_('Error'), _("Error when retrieving the account at line level."))
611+ line_account = account_obj.browse(cr, uid, line_account_id,
612+ fields_to_fetch=['activation_date', 'inactivation_date'], context=context)
613+ if inv_posting_date < line_account.activation_date or \
614+ (line_account.inactivation_date and inv_posting_date >= line_account.inactivation_date):
615+ raise osv.except_osv(_('Error'), _('The account "%s - %s" is inactive.') % (line_account.code, line_account.name))
616+ uom_id = False
617+ uom_data = inv_line.get('uos_id', {})
618+ if uom_data:
619+ uom_name = uom_data.get('name', '')
620+ uom_ids = product_uom_obj.search(cr, uid, [('name', '=', uom_name)], limit=1, context=context)
621+ if not uom_ids:
622+ raise osv.except_osv(_('Error'), _("Unit of Measure %s not found.") % uom_name)
623+ uom_id = uom_ids[0]
624+ quantity = inv_line.get('quantity', 0.0)
625+ inv_line_vals = {
626+ 'invoice_id': inv_id,
627+ 'account_id': line_account_id,
628+ 'name': line_name,
629+ 'quantity': quantity,
630+ 'price_unit': inv_line.get('price_unit', 0.0),
631+ 'discount': inv_line.get('discount', 0.0),
632+ 'product_id': product_id,
633+ 'uos_id': uom_id,
634+ }
635+ if from_supply and inv_linked_po:
636+ # fill in the AD at line level if applicable
637+ # search the matching between PO line and invoice line based on description/product/quantity
638+ matching_po_line = False
639+ for po_line in inv_linked_po.order_line:
640+ if po_line.name == line_name and po_line.product_id and po_line.product_id.id == product_id and \
641+ po_line.product_qty == quantity and po_line.state not in ('draft', 'cancel', 'cancel_r'):
642+ matching_po_line = po_line
643+ break
644+ if matching_po_line:
645+ po_line_distrib = matching_po_line.analytic_distribution_id
646+ self._create_analytic_distrib(cr, uid, inv_line_vals, po_line_distrib, context=context) # update inv_line_vals
647+ inv_line_obj.create(cr, uid, inv_line_vals, context=context)
648+
649+ def create_invoice_from_sync(self, cr, uid, source, invoice_data, context=None):
650+ """
651+ Creates automatic counterpart invoice at synchro time.
652+ Intermission workflow: an IVO sent generates an IVI
653+ Intersection workflow: an STV sent generates an SI
654+ """
655+ self._logger.info("+++ Create an account.invoice in %s matching the one sent by %s" % (cr.dbname, source))
656+ if context is None:
657+ context = {}
658+ journal_obj = self.pool.get('account.journal')
659+ currency_obj = self.pool.get('res.currency')
660+ partner_obj = self.pool.get('res.partner')
661+ user_obj = self.pool.get('res.users')
662+ po_obj = self.pool.get('purchase.order')
663+ stock_picking_obj = self.pool.get('stock.picking')
664+ invoice_dict = invoice_data.to_dict()
665+ # the counterpart instance must exist and be active
666+ partner_ids = partner_obj.search(cr, uid, [('name', '=', source), ('active', '=', True)], limit=1, context=context)
667+ if not partner_ids:
668+ raise osv.except_osv(_('Error'), _("The partner %s doesn't exist or is inactive.") % source)
669+ partner_id = partner_ids[0]
670+ partner = partner_obj.browse(cr, uid, partner_id, fields_to_fetch=['property_account_payable', 'name'], context=context)
671+ journal_type = invoice_dict.get('journal_id', {}).get('type', '')
672+ if not journal_type or journal_type not in ('sale', 'intermission'):
673+ raise osv.except_osv(_('Error'), _("Impossible to retrieve the journal type, or the journal type is incorrect."))
674+ currency_name = invoice_dict.get('currency_id', {}).get('name', '')
675+ if not currency_name:
676+ raise osv.except_osv(_('Error'), _("Impossible to retrieve the currency."))
677+ currency_ids = currency_obj.search(cr, uid, [('name', '=', currency_name), ('currency_table_id', '=', False),
678+ ('active', '=', True)], limit=1, context=context)
679+ if not currency_ids:
680+ raise osv.except_osv(_('Error'), _("Currency %s not found or inactive.") % currency_name)
681+ currency_id = currency_ids[0]
682+ number = invoice_dict.get('number', '')
683+ state = invoice_dict.get('state', '') # note that we get the real state as the doc can be beyond the "open" state at sync. time
684+ doc_date = invoice_dict.get('document_date', time.strftime('%Y-%m-%d'))
685+ posting_date = invoice_dict.get('date_invoice', time.strftime('%Y-%m-%d'))
686+ description = invoice_dict.get('name', '')
687+ source_doc = invoice_dict.get('origin', '')
688+ from_supply = invoice_dict.get('from_supply', False)
689+ inv_lines = invoice_dict.get('invoice_line', [])
690+ po = False
691+ vals = {}
692+ # STV in sending instance: generates an SI in the receiving instance
693+ if journal_type == 'sale':
694+ pur_journal_ids = journal_obj.search(cr, uid, [('type', '=', 'purchase'), ('is_current_instance', '=', True)], limit=1, context=context)
695+ if not pur_journal_ids:
696+ raise osv.except_osv(_('Error'), _("No Purchase journal found for the current instance."))
697+ # for the SI use the Account Payable of the partner
698+ si_account = partner.property_account_payable
699+ if not si_account or posting_date < si_account.activation_date or \
700+ (si_account.inactivation_date and posting_date >= si_account.inactivation_date):
701+ raise osv.except_osv(_('Error'), _("Account Payable not found or inactive for the partner %s.") % partner.name)
702+ vals.update(
703+ {
704+ 'journal_id': pur_journal_ids[0],
705+ 'account_id': si_account.id,
706+ 'type': 'in_invoice',
707+ 'is_direct_invoice': False,
708+ 'is_inkind_donation': False,
709+ 'is_debit_note': False,
710+ 'is_intermission': False,
711+ }
712+ )
713+ # IVO in sending instance: generates an IVI in the receiving instance
714+ elif journal_type == 'intermission':
715+ int_journal_ids = journal_obj.search(cr, uid, [('type', '=', 'intermission'), ('is_current_instance', '=', True)], limit=1, context=context)
716+ if not int_journal_ids:
717+ raise osv.except_osv(_('Error'), _("No Intermission journal found for the current instance."))
718+ # for the IVI use the Intermission counterpart account from the Company form
719+ ivi_account = user_obj.browse(cr, uid, uid, fields_to_fetch=['company_id'], context=context).company_id.intermission_default_counterpart
720+ if not ivi_account or posting_date < ivi_account.activation_date or \
721+ (ivi_account.inactivation_date and posting_date >= ivi_account.inactivation_date):
722+ raise osv.except_osv(_('Error'), _("The Intermission counterpart account is missing in the Company form or is inactive."))
723+ vals.update(
724+ {
725+ 'journal_id': int_journal_ids[0],
726+ 'account_id': ivi_account.id,
727+ 'type': 'in_invoice',
728+ 'is_inkind_donation': False,
729+ 'is_debit_note': False,
730+ 'is_intermission': True,
731+ }
732+ )
733+ # common fields whatever the invoice type
734+ if from_supply:
735+ po_id = False
736+ po_number = ''
737+ fo_number = ''
738+ ship_or_out_ref = ''
739+ main_in = False
740+ # extract PO number, and Shipment or Simple Out ref, from refs looking like:
741+ # "se_HQ2C1.19/se_HQ2/HT101/PO00001 : SHIP/00002-01" or "se_HQ1C2.19/se_HQ1/HT201/PO00003 : OUT/00001"
742+ inv_name_split = description.split()
743+ if inv_name_split:
744+ po_number = inv_name_split[0].split('.')[-1]
745+ po_ids = po_obj.search(cr, uid, [('name', '=', po_number)], limit=1, context=context)
746+ if po_ids:
747+ po_id = po_ids[0]
748+ ship_or_out_ref = inv_name_split[-1]
749+ # extract FO number from source docs looking like:
750+ # "SHIP/00001-04:19/se_HQ1/HT101/FO00007" or "OUT/00003:19/se_HQ1/HT101/FO00008"
751+ inv_source_doc_split = source_doc.split(':')
752+ if inv_source_doc_split:
753+ fo_number = inv_source_doc_split[-1]
754+ if po_id:
755+ po = po_obj.browse(cr, uid, po_id, fields_to_fetch=['picking_ids', 'analytic_distribution_id', 'order_line'], context=context)
756+ shipment_ref = "%s.%s" % (source or '', ship_or_out_ref or '')
757+ # get the "main" IN
758+ main_in_ids = stock_picking_obj.search(cr, uid,
759+ [('id', 'in', [picking.id for picking in po.picking_ids]),
760+ ('shipment_ref', '=', shipment_ref)],
761+ limit=1, context=context)
762+ if main_in_ids:
763+ main_in = stock_picking_obj.browse(cr, uid, main_in_ids[0], fields_to_fetch=['name'], context=context)
764+ # fill in the Analytic Distribution
765+ # at header level if applicable
766+ po_distrib = po.analytic_distribution_id
767+ self._create_analytic_distrib(cr, uid, vals, po_distrib, context=context) # update vals
768+ # note: in case a FO would have been manually created the PO and IN would be missing in the ref/source doc,
769+ # but the same codification is used so it's visible that sthg is missing
770+ description = "%s.%s : %s" % (source, fo_number, main_in and main_in.name or '') # e.g. se_HQ1C1.19/se_HQ1/HT101/FO00008 : IN/00009
771+ source_doc = "%s:%s" % (main_in and main_in.name or '', po_id and po_number or '') # e.g. IN/00009:19/se_HQ1/HT201/PO00009
772+ vals.update(
773+ {
774+ 'partner_id': partner_id,
775+ 'currency_id': currency_id,
776+ 'document_date': doc_date,
777+ 'date_invoice': posting_date,
778+ 'name': description,
779+ 'origin': source_doc,
780+ 'counterpart_inv_number': number,
781+ 'counterpart_inv_status': state,
782+ 'from_supply': from_supply,
783+ 'synced': True,
784+ }
785+ )
786+ inv_id = self.create(cr, uid, vals, context=context)
787+ if inv_id:
788+ self._create_invoice_lines(cr, uid, inv_lines, inv_id, posting_date, po, from_supply, context=context)
789+ if journal_type == 'sale':
790+ self._logger.info("SI No. %s created successfully." % inv_id)
791+ elif journal_type == 'intermission':
792+ self._logger.info("IVI No. %s created successfully." % inv_id)
793+
794+ def update_counterpart_inv(self, cr, uid, source, invoice_data, state, context=None):
795+ """
796+ Updates the Counterpart Invoice Number and Status (to be triggered at synchro time)
797+
798+ For the record:
799+ In most cases the state "Open" of the Out Invoices will be updated following both msg rules "create_invoice_from_sync" and
800+ "update_counterpart_inv_opened". However "update_counterpart_inv_opened" can't be skipped to cover use cases such as:
801+ - in C1: open IVO and reconcile the related JI manually: IVO is in Paid state
802+ - sync from C1 to C2: the counterpart inv. status in the IVI generated is directly: Paid
803+ - in C1: unreconcile IVO JI. The IVO is back to Open.
804+ - sync from C1 to C2: the counterpart inv. status in the related IVI is updated to: Open
805+ """
806+ self._logger.info("+++ Update Counterpart Invoice data from %s" % source)
807+ if context is None:
808+ context = {}
809+ invoice_dict = invoice_data.to_dict()
810+ number = invoice_dict.get('number', '')
811+ counterpart_inv_number = invoice_dict.get('counterpart_inv_number', '')
812+ if state:
813+ vals = {
814+ 'counterpart_inv_status': state,
815+ }
816+ if number:
817+ vals.update(
818+ {
819+ 'counterpart_inv_number': number,
820+ }
821+ )
822+ inv_ids = []
823+ if counterpart_inv_number:
824+ inv_ids = self.search(cr, uid, [('number', '=', counterpart_inv_number)], limit=1, context=context)
825+ elif not counterpart_inv_number and number:
826+ # use case where the state of the IVO/STV is updated before the related IVI/SI has been opened
827+ inv_ids = self.search(cr, uid, [('counterpart_inv_number', '=', number)], limit=1, context=context)
828+ if inv_ids:
829+ self.write(cr, uid, inv_ids[0], vals, context=context)
830+ # note that the "Counterpart Inv. Number" received is the "Number" of the invoice updated!
831+ self._logger.info("account.invoice %s: Counterpart Invoice %s set to %s" % (counterpart_inv_number, number, state))
832+
833+ def update_counterpart_inv_opened(self, cr, uid, source, invoice_data, context=None):
834+ self.update_counterpart_inv(cr, uid, source, invoice_data, 'open', context=context)
835+
836+ def update_counterpart_inv_paid(self, cr, uid, source, invoice_data, context=None):
837+ self.update_counterpart_inv(cr, uid, source, invoice_data, 'paid', context=context)
838+
839+ def update_counterpart_inv_cancelled(self, cr, uid, source, invoice_data, context=None):
840+ self.update_counterpart_inv(cr, uid, source, invoice_data, 'cancel', context=context)
841+
842+
843+account_invoice_sync()
844+
845+
846+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
847
848=== modified file 'bin/addons/account_override/account_invoice_view.xml'
849--- bin/addons/account_override/account_invoice_view.xml 2018-02-01 13:22:25 +0000
850+++ bin/addons/account_override/account_invoice_view.xml 2019-07-15 07:18:58 +0000
851@@ -43,7 +43,7 @@
852 <field name="line_number"/>
853 </xpath>
854 <xpath expr="//field[@name='date_invoice']" position="before">
855- <field name="document_date"/>
856+ <field name="document_date" attrs="{'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
857 </xpath>
858 <xpath expr="//field[@name='period_id']" position="replace">
859 </xpath>
860@@ -52,7 +52,9 @@
861 <button name='invoice_cancel' position="replace">
862 <!-- The widget boolean permit to the attrs to work efficiently -->
863 <field name="purchase_ids" invisible="1" widget="boolean" readonly="1" />
864- <button name="invoice_cancel" string="Cancel" icon="gtk-cancel" attrs="{'invisible': ['|', ('state', 'in', ['open', 'paid']), ('purchase_ids', '!=', False)]}" confirm="You are about to cancel this invoice. Do you want to proceed?"/>
865+ <button name="invoice_cancel" string="Cancel" icon="gtk-cancel"
866+ attrs="{'invisible': ['|', '|', ('state', 'in', ['open', 'paid']), ('purchase_ids', '!=', False), ('synced', '=', True)]}"
867+ confirm="You are about to cancel this invoice. Do you want to proceed?"/>
868 </button>
869 <xpath expr="//button[@name='invoice_open']" position="after" >
870 <button name="button_split_invoice" states="draft,proforma2" type="object" string="Split Invoice" icon="gtk-cut"/>
871@@ -482,13 +484,22 @@
872 <field name="type" invisible="1"/>
873 <field name="is_intermission" invisible="1"/>
874 <!-- US-370: use the change currency button -->
875- <field name="currency_id" width="50"/>
876- <button name="%(account.action_account_change_currency)d" type="action" icon="terp-stock_effects-object-colorize" string="Change" attrs="{'invisible':[('state','!=','draft')]}" />
877+ <field name="currency_id" width="50" attrs="{'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
878+ <button name="%(account.action_account_change_currency)d" type="action"
879+ icon="terp-stock_effects-object-colorize" string="Change"
880+ attrs="{'invisible': [('state', '!=', 'draft')],
881+ 'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}" />
882 <newline/>
883- <field string="Partner" name="partner_id" on_change="onchange_partner_id(type,partner_id,date_invoice,payment_term, partner_bank_id,company_id, False, is_intermission, False, False, account_id)" domain="[('by_invoice_type', '=', True), ('partner_type', '=', 'intermission')]" context="{'type': context.get('type')}"/>
884+ <field string="Partner" name="partner_id"
885+ on_change="onchange_partner_id(type,partner_id, date_invoice,payment_term, partner_bank_id, company_id, False, is_intermission, False, False, account_id)"
886+ domain="[('by_invoice_type', '=', True), ('partner_type', '=', 'intermission')]"
887+ context="{'type': context.get('type')}"
888+ attrs="{'readonly': ['|',
889+ '&amp;', ('type', '=', 'in_invoice'), ('synced', '=', True),
890+ '&amp;', ('type', '=', 'out_invoice'), ('from_supply', '=', True)]}"/>
891 <field domain="[('partner_id','=',partner_id)]" name="address_invoice_id" string="Partner address"/>
892 <newline/>
893- <field name="document_date" string="I/MI voucher date"/>
894+ <field name="document_date" string="I/MI voucher date" attrs="{'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
895 <field name="date_invoice"/>
896 <newline/>
897 <group colspan="8" col="8" attrs="{'invisible': [('analytic_distribution_id', '=', False)]}">
898@@ -505,13 +516,19 @@
899 <notebook colspan="4">
900 <page string="Invoice">
901 <field name="account_id" domain="[('company_id', '=', company_id), ('restricted_area', '=', 'intermission_header')]"/>
902- <field name="name" string="Reference"/>
903+ <field name="name" string="Reference" attrs="{'readonly': [('from_supply', '=', True)]}"/>
904 <newline/>
905 <group name="import" string=" Import Lines " colspan="4" col="4" attrs="{'invisible':[('state', '!=', 'draft')]}">
906- <button name="wizard_import_si_line" string="Import lines" icon="gtk-dnd" type="object" attrs="{'invisible':[('state', '!=', 'draft')]}"/>
907+ <button name="wizard_import_si_line" string="Import lines" icon="gtk-dnd" type="object"
908+ attrs="{'invisible': [('state', '!=', 'draft')],
909+ 'readonly': ['|',
910+ ('from_supply', '=', True),
911+ '&amp;', ('type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
912 </group>
913- <field colspan="4" name="invoice_line" nolabel="1" widget="one2many_list" context="{'is_intermission': True}">
914- <tree string="Intermission Voucher Lines">
915+ <field colspan="4" name="invoice_line" nolabel="1" widget="one2many_list" context="{'is_intermission': True, 'from_inv_form': True}">
916+ <tree string="Intermission Voucher Lines"
917+ colors="red:analytic_distribution_state == 'invalid' or inactive_product == True;black:inactive_product == False and analytic_distribution_state in ('none', 'valid')">
918+ <field name="line_number"/>
919 <field name="is_corrected" invisible="1"/>
920 <button name="button_open_analytic_lines" string="Have been corrected" type="object" icon="terp-mail-" attrs="{'invisible': [('is_corrected', '=', False)]}"/>
921 <field name="name"/>
922@@ -523,7 +540,7 @@
923 <field name="analytic_distribution_state" invisible="1"/>
924 <field name="have_analytic_distribution_from_header" invisible="1"/>
925 <field name="quantity"/>
926- <field name="uos_id" string="UOM" invisible="1" />
927+ <field name="uos_id" string="UOM"/>
928 <field name="price_unit"/>
929 <field name="price_subtotal"/>
930 <field name="inactive_product" invisible="1" />
931@@ -532,12 +549,42 @@
932 <form string="Intermission Voucher Lines">
933 <notebook>
934 <page string="Line">
935- <field name="name" default_focus="1"/>
936+ <field name="from_supply" invisible="1"/>
937+ <field name="synced" invisible="1"/>
938+ <field name="invoice_type" invisible="1"/>
939+ <field name="product_id"
940+ on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.address_invoice_id, parent.currency_id, {'company_id': parent.company_id})"
941+ default_focus="1"
942+ attrs="{'readonly': ['|',
943+ ('from_supply', '=', True),
944+ '&amp;', ('invoice_type', '=', 'in_invoice'), ('synced', '=', True)]}" />
945+ <field name="uos_id"
946+ on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.address_invoice_id, parent.currency_id, {'company_id': parent.company_id})"
947+ attrs="{'readonly': ['|',
948+ ('from_supply', '=', True),
949+ '&amp;', ('invoice_type', '=', 'in_invoice'), ('synced', '=', True)]}"
950+ />
951+ <field name="quantity" attrs="{'readonly': ['|',
952+ ('from_supply', '=', True),
953+ '&amp;', ('invoice_type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
954+ <field name="price_unit" attrs="{'readonly': [('invoice_type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
955+ <field name="discount" groups="base.group_extended" attrs="{'readonly': [('invoice_type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
956+ <field name="name" colspan="4"/>
957 <field name="account_id" domain="[('restricted_area', '=', 'intermission_lines')]"/>
958- <field name="quantity"/>
959- <field name="uos_id" string="UOM" invisible="1"/>
960- <field name="price_unit"/>
961- <field name="price_subtotal"/>
962+ <group colspan="6">
963+ <field name="analytic_distribution_state" invisible="1"/>
964+ <field name="newline" invisible="1" />
965+ <field name="is_allocatable" invisible="1"/>
966+ <group colspan="2" col="4">
967+ <button name="button_analytic_distribution" string="Change analytic distribution"
968+ type="object" icon="terp-check" context="context" colspan="1"
969+ attrs="{'invisible': ['|','|', ('newline', '=', True), ('analytic_distribution_state', '!=', 'valid'), ('is_allocatable', '=', False)]}" />
970+ <button name="button_analytic_distribution" string="Change analytic distribution"
971+ type="object" icon="terp-emblem-important" context="context" colspan="1"
972+ attrs="{'invisible': ['|','|', ('newline', '=', True), ('analytic_distribution_state', '=', 'valid'), ('is_allocatable', '=', False)]}" />
973+ <field name="analytic_distribution_state_recap" attrs="{'invisible': [('is_allocatable', '=', False)]}"/>
974+ </group>
975+ </group>
976 </page>
977 <page string="Notes">
978 <field name="note" nolabel="1" colspan="4"/>
979@@ -550,12 +597,26 @@
980 <field name="state"/>
981 <field name="amount_total"/>
982 </group>
983- <group col="2" colspan="4" states="draft">
984+ <group col="6" colspan="4" states="draft">
985 <label string=""/>
986- <button name="invoice_open" type="object" states="draft" string="Validate" icon="gtk-go-forward"/>
987+ <!-- add a confirmation step only for IVOs when "synced" isn't selected -->
988+ <button name="invoice_open" type="object" string="Validate"
989+ icon="gtk-go-forward"
990+ attrs="{'invisible': ['|',
991+ ('state', '!=', 'draft'),
992+ '&amp;', ('type', '=', 'out_invoice'), ('synced', '=', False)]}"/>
993+ <button name="invoice_open_with_confirmation" type="object" string="Validate"
994+ icon="gtk-go-forward"
995+ confirm="Are you sure you want to validate this invoice without synchronization?"
996+ attrs="{'invisible': ['|', '|',
997+ ('state', '!=', 'draft'),
998+ ('type', '!=', 'out_invoice'),
999+ ('synced', '=', True)]}"/>
1000 </group>
1001- <group col="2" colspan="4" states="open,paid">
1002+ <group col="6" colspan="4" states="open,paid">
1003 <label string=""/>
1004+ <button name="%(account.action_account_invoice_refund)d" type="action" string="Refund" icon="gtk-execute"
1005+ attrs="{'invisible': [('state', '!=', 'open')]}"/>
1006 <button name="%(account.account_invoices)d" string="Print Intermission Voucher" type="action" icon="gtk-print" states="open,paid"/>
1007 </group>
1008 </page>
1009@@ -563,15 +624,29 @@
1010 <field name="company_id" on_change="onchange_company_id(company_id,partner_id,type,invoice_line,currency_id)" widget="selection" groups="base.group_multi_company"/>
1011 <newline/>
1012 <field name="date_due"/>
1013- <field name="user_id"/>
1014+ <field name="user_id"
1015+ attrs="{'readonly': ['|',
1016+ '&amp;', ('type', '=', 'in_invoice'), ('synced', '=', True),
1017+ '&amp;', ('type', '=', 'out_invoice'), ('from_supply', '=', True)]}"/>
1018 <newline/>
1019 <field domain="[('partner_id.ref_companies', 'in', [company_id])]" name="partner_bank_id" />
1020- <field name="origin"/>
1021+ <field name="origin" attrs="{'readonly': [('from_supply', '=', True)]}"/>
1022 <field colspan="4" domain="[('partner_id','=',partner_id)]" name="address_contact_id" />
1023 <field name="move_id" />
1024 <separator colspan="4" string="Additional Information"/>
1025 <field colspan="4" name="comment" nolabel="1"/>
1026 </page>
1027+ <page string="Counterpart Invoice">
1028+ <field name="from_supply" invisible="1"/>
1029+ <field name="synced"
1030+ attrs="{'readonly': ['|', '|',
1031+ ('from_supply', '=', True),
1032+ ('state', '!=', 'draft'),
1033+ ('type', '=', 'in_invoice')]}"/> <!-- IVI can never be ticked as "Synced" manually -->
1034+ <newline/>
1035+ <field name="counterpart_inv_number"/>
1036+ <field name="counterpart_inv_status"/>
1037+ </page>
1038 </notebook>
1039 </form>
1040 </field>
1041@@ -610,7 +685,11 @@
1042 <button name="invoice_proforma2" position="replace"/>
1043 <xpath expr="/form/notebook/page[@string='Invoice']/field[@name='invoice_line']" position="before" >
1044 <group name="import" string=" Import Lines " colspan="4" col="4" attrs="{'invisible':[('state', '!=', 'draft')]}">
1045- <button name="wizard_import_si_line" string="Import lines" icon="gtk-dnd" type="object" attrs="{'invisible':[('state', '!=', 'draft')]}"/>
1046+ <button name="wizard_import_si_line" string="Import lines" icon="gtk-dnd" type="object"
1047+ attrs="{'invisible': [('state', '!=', 'draft')],
1048+ 'readonly': [('type', '=', 'out_invoice'),
1049+ ('from_supply', '=', True),
1050+ ('partner_type', 'in', ('intermission', 'section'))]}"/>
1051 </group>
1052 </xpath>
1053 <xpath expr="/form/notebook/page[@string='Invoice']/group[3]" position="replace">
1054@@ -623,11 +702,27 @@
1055 <field name="residual"/>
1056 <field name="is_debit_note" invisible="1"/>
1057 </group>
1058+ <field name="partner_type" invisible="1"/>
1059 <group col="8" colspan="4">
1060 <button name="invoice_cancel" states="proforma2,open" string="Cancel" icon="gtk-cancel"/>
1061 <button name="%(account.action_account_invoice_refund)d" type='action' string='Refund' icon="gtk-execute" attrs="{'invisible': ['|', ('state', 'in', ['draft']), ('type', 'in', ['in_refund', 'out_refund'])]}"/>
1062 <button name='%(account.action_account_state_open)d' type='action' string='Re-Open' states='paid' icon="gtk-convert" groups="base.group_no_one"/>
1063- <button name="invoice_open" states="draft,proforma2" string="Validate" icon="gtk-go-forward" type="object"/>
1064+ <!-- add a confirmation step only for STV (not for Customer Refunds)
1065+ when the partner type is compatible with a synchro but "synced" isn't ticked -->
1066+ <button name="invoice_open" type="object" string="Validate"
1067+ icon="gtk-go-forward"
1068+ attrs="{'invisible': ['|',
1069+ ('state', '!=', 'draft'),
1070+ '&amp;', '&amp;',
1071+ ('type', '=', 'out_invoice'), ('synced', '=', False), ('partner_type', 'in', ('intermission', 'section')) ]}"/>
1072+ <button name="invoice_open_with_confirmation" type="object" string="Validate"
1073+ icon="gtk-go-forward"
1074+ confirm="Are you sure you want to validate this invoice without synchronization?"
1075+ attrs="{'invisible': ['|', '|', '|',
1076+ ('state', '!=', 'draft'),
1077+ ('type', '!=', 'out_invoice'),
1078+ ('synced', '=', True),
1079+ ('partner_type', 'not in', ('intermission', 'section'))]}"/>
1080 <button name="%(account.account_invoices)d" string="Print Invoice" type="action" icon="gtk-print" states="open,paid,proforma,sale,proforma2"/>
1081 </group>
1082 </xpath>
1083@@ -813,7 +908,7 @@
1084 <field eval="False" name="view_id"/>
1085 <!-- US-2704 if the domain change, do not forget to update the domain of account_board_supplier_invoice_draft in finance/board_account_view.xml -->
1086 <field name="domain">[('type','=','in_invoice'), ('is_direct_invoice', '=', False), ('is_inkind_donation', '=', False), ('is_debit_note', "=", False), ('is_intermission', '=', False)]</field>
1087- <field name="context">{'type':'in_invoice', 'journal_type': 'purchase'}</field>
1088+ <field name="context">{'type': 'in_invoice', 'journal_type': 'purchase', 'from_inv_form': True}</field>
1089 <field name="search_view_id" ref="account.view_account_invoice_filter"/>
1090 <field name="help">With Supplier Invoices you can enter and manage invoices issued by your suppliers.
1091 OpenERP can also generate draft invoices automatically from purchase orders or receipts. This way, you can control the invoice
1092
1093=== modified file 'bin/addons/account_override/invoice.py'
1094--- bin/addons/account_override/invoice.py 2019-05-21 08:19:49 +0000
1095+++ bin/addons/account_override/invoice.py 2019-07-15 07:18:58 +0000
1096@@ -28,12 +28,14 @@
1097 from tools.translate import _
1098 from lxml import etree
1099 from datetime import datetime
1100+from msf_partner import PARTNER_TYPE
1101 import re
1102 import netsvc
1103
1104
1105 import decimal_precision as dp
1106
1107+
1108 class account_invoice(osv.osv):
1109 _name = 'account.invoice'
1110 _inherit = 'account.invoice'
1111@@ -251,6 +253,8 @@
1112 'st_lines': fields.one2many('account.bank.statement.line', 'invoice_id', string="Register lines", readonly=True, help="Register lines that have a link to this invoice."),
1113 'can_merge_lines': fields.function(_get_can_merge_lines, method=True, type='boolean', string='Can merge lines ?'),
1114 'is_merged_by_account': fields.boolean("Is merged by account"),
1115+ 'partner_type': fields.related('partner_id', 'partner_type', string='Partner Type', type='selection',
1116+ selection=PARTNER_TYPE, readonly=True, store=False),
1117 }
1118
1119 _defaults = {
1120@@ -814,6 +818,35 @@
1121 'view_id': supplier_view_id}
1122 return super(account_invoice, self).log(cr, uid, inv_id, message, secondary, action_xmlid, local_ctx)
1123
1124+ def _check_tax_allowed(self, cr, uid, ids, context=None):
1125+ """
1126+ Raises an error if a tax is used with an Intermission or Intersection partner
1127+ """
1128+ if context is None:
1129+ context = {}
1130+ if isinstance(ids, (int, long)):
1131+ ids = [ids]
1132+ warning_msg = _('Taxes are forbidden with Intermission and Intersection partners.')
1133+ for inv in self.browse(cr, uid, ids, fields_to_fetch=['partner_id', 'tax_line', 'invoice_line'], context=context):
1134+ if inv.partner_id.partner_type in ('intermission', 'section'):
1135+ if inv.tax_line:
1136+ raise osv.except_osv(_('Warning'), warning_msg)
1137+ for inv_line in inv.invoice_line:
1138+ if inv_line.invoice_line_tax_id:
1139+ raise osv.except_osv(_('Warning'), warning_msg)
1140+
1141+ def _check_sync_allowed(self, cr, uid, ids, context=None):
1142+ """
1143+ Raises an error if the doc is marked as Synced whereas the Partner is neither Intermission nor Intersection
1144+ """
1145+ if context is None:
1146+ context = {}
1147+ if isinstance(ids, (int, long)):
1148+ ids = [ids]
1149+ for inv in self.browse(cr, uid, ids, fields_to_fetch=['partner_id', 'synced'], context=context):
1150+ if inv.partner_id.partner_type not in ('intermission', 'section') and inv.synced:
1151+ raise osv.except_osv(_('Warning'), _('Synchronized invoices are allowed only with Intermission and Intersection partners.'))
1152+
1153 def invoice_open(self, cr, uid, ids, context=None):
1154 """
1155 No longer fills the date automatically, but requires it to be set
1156@@ -823,6 +856,8 @@
1157 context = {}
1158 self._check_invoice_merged_lines(cr, uid, ids, context=context)
1159 self.check_accounts_for_partner(cr, uid, ids, context=context)
1160+ self._check_tax_allowed(cr, uid, ids, context=context)
1161+ self._check_sync_allowed(cr, uid, ids, context=context)
1162
1163 # Prepare workflow object
1164 wf_service = netsvc.LocalService("workflow")
1165@@ -871,6 +906,12 @@
1166
1167 return True
1168
1169+ def invoice_open_with_confirmation(self, cr, uid, ids, context=None):
1170+ """
1171+ Simply calls "invoice_open" (asking for confirmation is done at form level)
1172+ """
1173+ return self.invoice_open(cr, uid, ids, context=context)
1174+
1175 def action_reconcile_imported_invoice(self, cr, uid, ids, context=None):
1176 """
1177 Reconcile each imported invoice with its attached invoice line
1178@@ -1107,6 +1148,21 @@
1179 self._check_header_account(cr, uid, inv_id, inv_type=inv_type, context=context)
1180 self._check_line_accounts(cr, uid, inv_id, inv_type=inv_type, context=context)
1181
1182+ def update_counterpart_inv_status(self, cr, uid, ids, context=None):
1183+ """
1184+ In case an IVO or STV, with an Intermission or Intersection partner and set as Synchronized, is being opened:
1185+ set the Counterpart Invoice Status to Draft automatically
1186+ """
1187+ if context is None:
1188+ context = {}
1189+ if isinstance(ids, (int, long)):
1190+ ids = [ids]
1191+ inv_fields = ['type', 'is_debit_note', 'synced', 'partner_type', 'counterpart_inv_status']
1192+ for inv in self.browse(cr, uid, ids, fields_to_fetch=inv_fields, context=context):
1193+ is_ivo_or_stv = inv.type == 'out_invoice' and not inv.is_debit_note
1194+ if is_ivo_or_stv and inv.synced and inv.partner_type in ('intermission', 'section') and not inv.counterpart_inv_status:
1195+ self.write(cr, uid, inv.id, {'counterpart_inv_status': 'draft'}, context=context)
1196+
1197 def action_open_invoice(self, cr, uid, ids, context=None, *args):
1198 """
1199 Give function to use when changing invoice to open state
1200@@ -1124,6 +1180,7 @@
1201 if not self.action_reconcile_imported_invoice(cr, uid, ids, context):
1202 return False
1203 self.check_domain_restrictions(cr, uid, ids, context) # raises an error if one unauthorized element is used
1204+ self.update_counterpart_inv_status(cr, uid, ids, context=context)
1205 return True
1206
1207
1208@@ -1629,6 +1686,13 @@
1209 relation="account.analytic.account", string='Funding Pool',
1210 states={'draft': [('readonly', False)]},
1211 help="Field used for import only"),
1212+ 'from_supply': fields.related('invoice_id', 'from_supply', type='boolean', string='From Supply', readonly=True, store=False),
1213+ 'synced': fields.related('invoice_id', 'synced', type='boolean', string='Synchronized', readonly=True, store=False),
1214+ 'invoice_type': fields.related('invoice_id', 'type', string='Invoice Type', type='selection', readonly=True, store=False,
1215+ selection=[('out_invoice', 'Customer Invoice'),
1216+ ('in_invoice', 'Supplier Invoice'),
1217+ ('out_refund', 'Customer Refund'),
1218+ ('in_refund', 'Supplier Refund')]),
1219 }
1220
1221 _defaults = {
1222@@ -1661,6 +1725,23 @@
1223 if abs(qty) >= too_big_amount or abs(pu) >= too_big_amount or abs(discount) >= too_big_amount or abs(subtotal) >= too_big_amount:
1224 raise osv.except_osv(_('Error'), _('Line "%s": one of the numbers entered is more than 10 digits.') % inv_line.name)
1225
1226+ def _check_automated_invoice(self, cr, uid, invoice_id, context=None):
1227+ """
1228+ Prevents the creation of manual inv. lines if the related invoice has been generated via Sync. or
1229+ by a Supply workflow (for Intermission/Intersection partners)
1230+ """
1231+ if context is None:
1232+ context = {}
1233+ inv_obj = self.pool.get('account.invoice')
1234+ if invoice_id:
1235+ inv_fields = ['from_supply', 'synced', 'type', 'is_inkind_donation', 'partner_type']
1236+ inv = inv_obj.browse(cr, uid, invoice_id, fields_to_fetch=inv_fields, context=context)
1237+ ivi_or_si_synced = inv.type == 'in_invoice' and not inv.is_inkind_donation and inv.synced
1238+ intermission_or_section_from_supply = inv.partner_type in ('intermission', 'section') and inv.from_supply
1239+ if context.get('from_inv_form') and (ivi_or_si_synced or intermission_or_section_from_supply):
1240+ raise osv.except_osv(_('Error'), _('This document has been generated via a Supply workflow or via synchronization. '
1241+ 'You can\'t add lines manually.'))
1242+
1243 def create(self, cr, uid, vals, context=None):
1244 """
1245 Give a line_number to invoice line.
1246@@ -1671,6 +1752,7 @@
1247 """
1248 if not context:
1249 context = {}
1250+ self._check_automated_invoice(cr, uid, vals.get('invoice_id'), context=context)
1251 # Create new number with invoice sequence
1252 if vals.get('invoice_id') and self._name in ['account.invoice.line']:
1253 invoice = self.pool.get('account.invoice').browse(cr, uid, vals['invoice_id'])
1254@@ -1766,9 +1848,10 @@
1255
1256 def unlink(self, cr, uid, ids, context=None):
1257 """
1258- If invoice is a Direct Invoice and is in draft state:
1259- - compute total amount (check_total field)
1260- - write total to the register line
1261+ - If invoice is a Direct Invoice and is in draft state:
1262+ - compute total amount (check_total field)
1263+ - write total to the register line
1264+ - Raise error msg if the related inv. has been generated via Sync. or by a Supply workflow (for Intermission/Intersection partners)
1265 """
1266 if not context:
1267 context = {}
1268@@ -1776,16 +1859,25 @@
1269 ids = [ids]
1270 # Fetch all invoice_id to check
1271 direct_invoice_ids = []
1272+ invoice_ids = []
1273 abst_obj = self.pool.get('account.bank.statement.line')
1274 for invl in self.browse(cr, uid, ids):
1275- if invl.invoice_id and invl.invoice_id.is_direct_invoice and invl.invoice_id.state == 'draft':
1276- direct_invoice_ids.append(invl.invoice_id.id)
1277- # find account_bank_statement_lines and used this to delete the account_moves and associated records
1278- absl_ids = abst_obj.search(cr, uid,
1279- [('invoice_id','=',invl.invoice_id.id)],
1280- order='NO_ORDER')
1281- if absl_ids:
1282- abst_obj.unlink_moves(cr, uid, absl_ids, context)
1283+ if invl.invoice_id and invl.invoice_id.id not in invoice_ids:
1284+ invoice = invl.invoice_id
1285+ invoice_ids.append(invoice.id) # check each invoice only once
1286+ is_ivi_or_si = invoice.type == 'in_invoice' and not invoice.is_inkind_donation
1287+ if (is_ivi_or_si and invoice.synced) or (invoice.from_supply and invoice.partner_type in ('intermission', 'section')):
1288+ # will be displayed when trying to delete lines manually / merge lines / or split invoices
1289+ raise osv.except_osv(_('Error'), _("This document has been generated via a Supply workflow or via synchronization. "
1290+ "Existing lines can't be deleted."))
1291+ if invoice.is_direct_invoice and invoice.state == 'draft':
1292+ direct_invoice_ids.append(invoice.id)
1293+ # find account_bank_statement_lines and use this to delete the account_moves and associated records
1294+ absl_ids = abst_obj.search(cr, uid,
1295+ [('invoice_id', '=', invoice.id)],
1296+ order='NO_ORDER')
1297+ if absl_ids:
1298+ abst_obj.unlink_moves(cr, uid, absl_ids, context)
1299 # Normal behaviour
1300 res = super(account_invoice_line, self).unlink(cr, uid, ids, context)
1301 # See all direct invoice
1302
1303=== modified file 'bin/addons/analytic_distribution/account_invoice_refund.py'
1304--- bin/addons/analytic_distribution/account_invoice_refund.py 2018-05-14 12:41:04 +0000
1305+++ bin/addons/analytic_distribution/account_invoice_refund.py 2019-07-15 07:18:58 +0000
1306@@ -43,8 +43,10 @@
1307 # in case of a DI refund from a register line use the dir_invoice_id in context
1308 doc_to_refund_id = context.get('dir_invoice_id', False) or (context.get('active_ids') and context['active_ids'][0])
1309 if doc_to_refund_id:
1310- source_type = obj_inv.read(cr, uid, doc_to_refund_id, ['type'], context=context)['type']
1311- if source_type in ('in_invoice', 'in_refund'):
1312+ source = obj_inv.read(cr, uid, doc_to_refund_id, ['type', 'is_intermission'], context=context)
1313+ if source['is_intermission']:
1314+ args = [('type', '=', 'intermission')]
1315+ elif source['type'] in ('in_invoice', 'in_refund'):
1316 args = [('type', '=', 'purchase_refund')]
1317 if user.company_id.instance_id:
1318 args.append(('is_current_instance','=',True))
1319@@ -72,7 +74,7 @@
1320 jtype = isinstance(context['journal_type'], list) and context['journal_type'][0] or context['journal_type']
1321 if jtype in ('sale', 'sale_refund'):
1322 jtype = 'sale_refund'
1323- else:
1324+ elif jtype != 'intermission': # for IVO/IVI keep using the Interm. journal
1325 jtype = 'purchase_refund'
1326 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1327 for field in res['fields']:
1328@@ -86,13 +88,32 @@
1329 _columns = {
1330 'date': fields.date('Posting date'),
1331 'document_date': fields.date('Document Date', required=True),
1332+ 'is_intermission': fields.boolean("Wizard opened from an Intermission Voucher", readonly=True)
1333 }
1334
1335+ def _get_refund(self, cr, uid, context=None):
1336+ """
1337+ Returns the default value for the 'filter_refund' field depending on the context
1338+ """
1339+ if context is None:
1340+ context = {}
1341+ if context.get('is_intermission', False):
1342+ return 'modify'
1343+ return 'refund' # note that only the "Refund" option is available in DI
1344+
1345+ def _get_is_intermission(self, cr, uid, context=None):
1346+ """
1347+ Returns True if the wizard has been opened from an Intermission Voucher
1348+ """
1349+ if context is None:
1350+ context = {}
1351+ return context.get('is_intermission', False)
1352+
1353 _defaults = {
1354 'document_date': _get_document_date,
1355- #UTP-961: refund DI: only refund option is available
1356- 'filter_refund': 'refund',
1357+ 'filter_refund': _get_refund,
1358 'journal_id': _get_journal, # US-193
1359+ 'is_intermission': _get_is_intermission,
1360 }
1361
1362 def _hook_fields_for_modify_refund(self, cr, uid, *args):
1363
1364=== modified file 'bin/addons/analytic_distribution/account_invoice_view.xml'
1365--- bin/addons/analytic_distribution/account_invoice_view.xml 2018-02-23 14:35:29 +0000
1366+++ bin/addons/analytic_distribution/account_invoice_view.xml 2019-07-15 07:18:58 +0000
1367@@ -120,10 +120,16 @@
1368 <xpath expr="//separator[@string='Taxes']" position="replace" />
1369 <xpath expr="//field[@name='invoice_line_tax_id']" position="replace">
1370 <field name="vat_ok" invisible="1" />
1371+ <field name="synced" invisible="1" />
1372 <group colspan="4" col="4" attrs="{'invisible': [('vat_ok', '=', False)]}">
1373 <separator colspan="4" string="Taxes"/>
1374- <field colspan="4" name="invoice_line_tax_id" context="{'type':parent.type}" domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]"
1375- nolabel="1"/>
1376+ <field colspan="4"
1377+ name="invoice_line_tax_id"
1378+ context="{'type': parent.type}"
1379+ domain="[('parent_id', '=', False), ('company_id', '=', parent.company_id)]"
1380+ nolabel="1"
1381+ attrs="{'readonly': [('invoice_type', '=', 'in_invoice'), ('synced', '=', True)]}"
1382+ />
1383 </group>
1384 </xpath>
1385 </data>
1386
1387=== modified file 'bin/addons/msf_audittrail/audittrail_invoice_data.yml'
1388--- bin/addons/msf_audittrail/audittrail_invoice_data.yml 2019-05-13 09:18:37 +0000
1389+++ bin/addons/msf_audittrail/audittrail_invoice_data.yml 2019-07-15 07:18:58 +0000
1390@@ -9,7 +9,8 @@
1391 # Create the rule
1392 fields = ['date_invoice', 'state', 'account_id', 'address_invoice_id', 'amount_to_pay', 'analytic_distribution_id',
1393 'check_total', 'comment', 'currency_id', 'fiscal_position', 'name', 'origin', 'partner_id',
1394- 'payment_ids', 'period_id', 'reference', 'reference_type', 'tax_line']
1395+ 'payment_ids', 'period_id', 'reference', 'reference_type', 'tax_line', 'synced', 'counterpart_inv_number',
1396+ 'counterpart_inv_status']
1397
1398 fields_ids = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=' ,'account.invoice'), ('name', 'in', fields)], context=context)
1399
1400
1401=== modified file 'bin/addons/msf_outgoing/msf_outgoing.py'
1402--- bin/addons/msf_outgoing/msf_outgoing.py 2019-05-10 13:59:15 +0000
1403+++ bin/addons/msf_outgoing/msf_outgoing.py 2019-07-15 07:18:58 +0000
1404@@ -1579,6 +1579,7 @@
1405 'fiscal_position': partner.property_account_position.id,
1406 'date_invoice': context.get('date_inv', False) or today,
1407 'user_id': uid,
1408+ 'from_supply': True,
1409 }
1410
1411 cur_id = shipment.pack_family_memory_ids[0].currency_id.id
1412@@ -1614,6 +1615,7 @@
1413 continue
1414
1415 if is_ivo or is_stv:
1416+ invoice_vals.update({'synced': True, }) # add "synced" tag for STV and IVO created from Supply flow
1417 origin_inv = 'origin' in invoice_vals and invoice_vals['origin'] or False
1418 fo = move and move.sale_line_id and move.sale_line_id.order_id or False
1419 new_origin = origin_inv and fo and "%s:%s" % (origin_inv, fo.name)
1420
1421=== modified file 'bin/addons/msf_profile/data/patches.xml'
1422--- bin/addons/msf_profile/data/patches.xml 2019-06-20 14:28:17 +0000
1423+++ bin/addons/msf_profile/data/patches.xml 2019-07-15 07:18:58 +0000
1424@@ -429,5 +429,10 @@
1425 <field name="method">us_6111_nr_field_closed_mar_2018</field>
1426 </record>
1427
1428+ <!-- UF14.0 -->
1429+ <record id="us_6076_set_inv_as_from_supply" model="patch.scripts">
1430+ <field name="method">us_6076_set_inv_as_from_supply</field>
1431+ </record>
1432+
1433 </data>
1434 </openerp>
1435
1436=== modified file 'bin/addons/msf_profile/i18n/fr_MF.po'
1437--- bin/addons/msf_profile/i18n/fr_MF.po 2019-06-20 14:24:33 +0000
1438+++ bin/addons/msf_profile/i18n/fr_MF.po 2019-07-15 07:18:58 +0000
1439@@ -8811,6 +8811,8 @@
1440 #: selection:wizard.import.product.line,state:0
1441 #: selection:product.mass.update,state:0
1442 #: selection:stock.expired.damaged.report,state:0
1443+#: selection:account.invoice,counterpart_inv_status:0
1444+#: selection:wizard.account.invoice,counterpart_inv_status:0
1445 #, python-format
1446 msgid "Draft"
1447 msgstr "Brouillon"
1448@@ -10423,6 +10425,8 @@
1449 #: selection:sale.donation.stock.moves,partner_type:0
1450 #: selection:sale.loan.stock.moves,partner_type:0
1451 #: selection:stock.picking,partner_type:0
1452+#: selection:account.invoice,partner_type:0
1453+#: selection:account.invoice.line,partner_type:0
1454 #: code:addons/msf_supply_doc_export/wizard/supplier_performance_wizard.py:32
1455 #: field:supplier.performance.wizard,partner_type_intermission:0
1456 msgid "Intermission"
1457@@ -11252,7 +11256,7 @@
1458
1459 #. module: account
1460 #: help:account.invoice.refund,filter_refund:0
1461-msgid "Refund invoice base on this type. You can not Modify and Cancel if the invoice is already reconciled"
1462+msgid "Refund invoice based on this type. You can not Modify and Cancel if the invoice is already reconciled"
1463 msgstr "Facture d'Avoirs basée sur ce type. Vous ne pouvez pas Modifier ni Annuler si la facture est déjà lettrée."
1464
1465 #. module: base
1466@@ -12219,7 +12223,7 @@
1467 msgid "No envoi.ini file found in given ZIP file!"
1468 msgstr "Pas de fichier envoi.ini trouvé dans le fichier ZP donné!"
1469
1470-#. modules: msf_processes, account, register_accounting
1471+#. modules: msf_processes, account, register_accounting, account_override
1472 #: view:account.invoice:0
1473 #: selection:account.invoice,type:0
1474 #: selection:account.invoice.report,type:0
1475@@ -12233,6 +12237,7 @@
1476 #: code:addons/register_accounting/account_bank_statement.py:2735
1477 #: view:wizard.account.invoice:0
1478 #: selection:wizard.account.invoice,type:0
1479+#: selection:account.invoice.line,invoice_type:0
1480 #, python-format
1481 msgid "Supplier Invoice"
1482 msgstr "Facture Fournisseur"
1483@@ -13330,7 +13335,7 @@
1484 msgid "Automatic entry"
1485 msgstr "Ecriture Automatique"
1486
1487-#. modules: msf_order_date, purchase, msf_partner, stock_override, purchase_override, msf_config_locations, sale, account_override, analytic_distribution, msf_outgoing, msf_supply_doc_export
1488+#. modules: msf_order_date, purchase, msf_partner, stock_override, purchase_override, msf_config_locations, sale, account_override, analytic_distribution, msf_outgoing, msf_supply_doc_export, account
1489 #: selection:shipment,partner_type:0
1490 #: selection:purchase.order.merged.line,po_partner_type_stored:0
1491 #: selection:sale.loan.stock.moves,partner_type:0
1492@@ -13348,6 +13353,8 @@
1493 #: selection:sale.donation.stock.moves,partner_type:0
1494 #: selection:sale.order.change.currency,partner_type:0
1495 #: selection:stock.picking,partner_type_stock_picking:0
1496+#: selection:account.invoice,partner_type:0
1497+#: selection:account.invoice.line,partner_type:0
1498 #: code:addons/msf_supply_doc_export/wizard/supplier_performance_wizard.py:30
1499 #: field:supplier.performance.wizard,partner_type_external:0
1500 msgid "External"
1501@@ -14950,8 +14957,9 @@
1502 msgid "BARBADOS DOLLAR"
1503 msgstr "BARBADOS DOLLAR"
1504
1505-#. modules: account, register_accounting
1506+#. modules: account, register_accounting, account_override
1507 #: selection:account.invoice,type:0
1508+#: selection:account.invoice.line,invoice_type:0
1509 #: selection:account.invoice.report,type:0
1510 #: report:account.invoice2:0
1511 #: selection:report.invoice.created,type:0
1512@@ -17436,12 +17444,13 @@
1513 msgid "Warning!"
1514 msgstr "Avertissement!"
1515
1516-#. modules: purchase, sale
1517+#. modules: purchase, sale, account_override
1518 #: code:addons/purchase/purchase_workflow.py:549
1519 #: code:addons/sale/sale_workflow.py:529
1520+#: code:addons/account_override/invoice.py:829
1521 #, python-format
1522-msgid "You can't use taxes with an intermission partner."
1523-msgstr "Vous ne pouvez pas utiliser de taxes avec un partenaire intermission."
1524+msgid "Taxes are forbidden with Intermission and Intersection partners."
1525+msgstr "Les taxes sont interdites avec les partenaires Intermission et Intersection."
1526
1527 #. module: account
1528 #: model:ir.actions.act_window,name:account.action_account_fiscal_position_form
1529@@ -21180,7 +21189,7 @@
1530 msgid "Free 1 Lines"
1531 msgstr "Option 1 Lignes"
1532
1533-#. modules: account_override, msf_outgoing, purchase_override, stock_override, sale, msf_order_date, purchase, msf_partner, msf_supply_doc_export
1534+#. modules: account_override, msf_outgoing, purchase_override, stock_override, sale, msf_order_date, purchase, msf_partner, msf_supply_doc_export, account
1535 #: field:account.account,has_partner_type_section:0
1536 #: selection:shipment,partner_type:0
1537 #: selection:purchase.order.merged.line,po_partner_type_stored:0
1538@@ -21188,6 +21197,8 @@
1539 #: selection:sale.loan.stock.moves,partner_type:0
1540 #: selection:stock.picking,partner_type:0
1541 #: selection:sale.order,partner_type:0
1542+#: selection:account.invoice,partner_type:0
1543+#: selection:account.invoice.line,partner_type:0
1544 #: view:res.partner:0
1545 #: selection:res.partner,partner_type:0
1546 #: selection:purchase.order,partner_type:0
1547@@ -27161,6 +27172,8 @@
1548 #: field:purchase.report,invoiced:0
1549 #: view:account.invoice:0
1550 #: selection:account.direct.invoice.wizard,state:0
1551+#: selection:account.invoice,counterpart_inv_status:0
1552+#: selection:wizard.account.invoice,counterpart_inv_status:0
1553 msgid "Paid"
1554 msgstr "Payé"
1555
1556@@ -45639,6 +45652,7 @@
1557
1558 #. modules: account, register_accounting
1559 #: field:account.invoice,tax_line:0
1560+#: field:account.invoice,tax_line_ids:0
1561 #: field:wizard.account.invoice,tax_line:0
1562 msgid "Tax Lines"
1563 msgstr "Lignes de Taxe"
1564@@ -47234,7 +47248,7 @@
1565 msgid "Metadata"
1566 msgstr "Métadonnées"
1567
1568-#. modules: msf_order_date, purchase, msf_outgoing, msf_partner, stock_override, purchase_override, sale, account_override, analytic_distribution, msf_supply_doc_export
1569+#. modules: msf_order_date, purchase, msf_outgoing, msf_partner, stock_override, purchase_override, sale, account_override, analytic_distribution, msf_supply_doc_export, account
1570 #: field:account.account,has_partner_type_esc:0
1571 #: view:account.commitment:0
1572 #: selection:sale.order,partner_type:0
1573@@ -47250,6 +47264,8 @@
1574 #: selection:sale.order.change.currency,partner_type:0
1575 #: selection:stock.picking,partner_type:0
1576 #: selection:stock.picking,partner_type_stock_picking:0
1577+#: selection:account.invoice,partner_type:0
1578+#: selection:account.invoice.line,partner_type:0
1579 #: code:addons/msf_supply_doc_export/wizard/supplier_performance_wizard.py:31
1580 #: field:supplier.performance.wizard,partner_type_esc:0
1581 msgid "ESC"
1582@@ -52060,9 +52076,11 @@
1583 msgid "Search Donation"
1584 msgstr "Rechercher Donation"
1585
1586-#. module: sale
1587+#. modules: sale, account_override, account
1588 #: report:addons/sale/report/sale_donation_stock_moves_report_xls.mako:127
1589 #: field:sale.donation.stock.moves,partner_type:0
1590+#: field:account.invoice,partner_type:0
1591+#: field:account.invoice.line,partner_type:0
1592 msgid "Partner Type"
1593 msgstr "Type de Partenaire"
1594
1595@@ -57043,8 +57061,9 @@
1596 msgid "Kit Composition List"
1597 msgstr "Liste de composition du Kit"
1598
1599-#. modules: account, register_accounting
1600+#. modules: account, register_accounting, account_override
1601 #: selection:account.invoice,type:0
1602+#: selection:account.invoice.line,invoice_type:0
1603 #: selection:account.invoice.report,type:0
1604 #: selection:report.invoice.created,type:0
1605 #: selection:wizard.account.invoice,type:0
1606@@ -63480,6 +63499,18 @@
1607 msgid "Cannot delete invoice(s) that are already opened or paid !"
1608 msgstr "Impossible de supprimer des factures qui sont déjà ouvertes ou payées !"
1609
1610+#. module: account
1611+#: code:addons/account/invoice.py:472
1612+#, python-format
1613+msgid "Cannot delete invoice(s) generated by a Supply workflow!"
1614+msgstr "Impossible de supprimer des factures générées par un flux \"Supply\" !"
1615+
1616+#. module: account
1617+#: code:addons/account/invoice.py:474
1618+#, python-format
1619+msgid "Cannot delete invoice(s) set as \"Synchronized\"!"
1620+msgstr "Impossible de supprimer des factures marquées comme \"Synchronisées\" !"
1621+
1622 #. module: stock
1623 #: view:physical.inventory:0
1624 msgid "Counting sheet"
1625@@ -66467,8 +66498,9 @@
1626 msgid "Remove all Instances"
1627 msgstr "Supprimer toutes les instances"
1628
1629-#. module: analytic_distribution
1630+#. modules: analytic_distribution, account_override
1631 #: view:account.invoice.line:0
1632+#: view:account.invoice:0
1633 msgid "Change analytic distribution"
1634 msgstr "Changer la distribution analytique"
1635
1636@@ -68529,7 +68561,7 @@
1637 #. module: account_override
1638 #: view:account.invoice:0
1639 msgid "You are about to cancel this invoice. Do you want to proceed?"
1640-msgstr "You are about to cancel this invoice. Do you want to proceed?"
1641+msgstr "Vous êtes sur le point d'annuler cette facture. Voulez-vous continuer ?"
1642
1643 #. module: procurement
1644 #: help:procurement.order,name:0
1645@@ -73872,6 +73904,7 @@
1646 #: code:addons/account_override/invoice.py:1484
1647 #: code:addons/account_override/invoice.py:1772
1648 #: code:addons/account_override/invoice.py:1775
1649+#: code:addons/account_override/account_invoice_sync.py:0
1650 #: code:addons/account_reconciliation/account_move_line.py:236
1651 #: code:addons/account_reconciliation/account_move_line.py:256
1652 #: code:addons/account_reconciliation/account_move_line.py:258
1653@@ -78469,6 +78502,8 @@
1654 #: selection:purchase.order,rfq_state:0
1655 #: view:tender:0
1656 #: selection:tender,state:0
1657+#: selection:account.invoice,counterpart_inv_status:0
1658+#: selection:wizard.account.invoice,counterpart_inv_status:0
1659 #: field:po.follow.up,cancel_ok:0
1660 #, python-format
1661 msgid "Cancelled"
1662@@ -88640,6 +88675,8 @@
1663 #: selection:wizard.import.cheque,state:0
1664 #: selection:wizard.import.invoice,state:0
1665 #: selection:wizard.register.creation,state:0
1666+#: selection:account.invoice,counterpart_inv_status:0
1667+#: selection:wizard.account.invoice,counterpart_inv_status:0
1668 #, python-format
1669 msgid "Open"
1670 msgstr "Ouvert"
1671@@ -89009,10 +89046,17 @@
1672
1673 #. module: account_override
1674 #: code:addons/account_override/invoice.py:747
1675+#: code:addons/account_override/account_invoice_sync.py:230
1676 #, python-format
1677 msgid "No Intermission journal found for the current instance."
1678 msgstr "Pas de journal Intermission trouvé pour l'instance actuelle."
1679
1680+#. module: account_override
1681+#: code:addons/account_override/account_invoice_sync.py:209
1682+#, python-format
1683+msgid "No Purchase journal found for the current instance."
1684+msgstr "Pas de journal des Achats trouvé pour l'instance actuelle."
1685+
1686 #. module: msf_profile
1687 #: field:email.configuration,smtp_server:0
1688 msgid "SMTP Server"
1689@@ -94980,6 +95024,24 @@
1690 msgid "Can not %s invoice which is already reconciled, invoice should be unreconciled first. You can only Refund this invoice"
1691 msgstr "Ne peut %s une facture qui est déjà lettrée, le lettrage de la facture devrait d'abord être annulé. Vous ne pouvez que Rembourser cette facture"
1692
1693+#. module: account
1694+#: code:addons/account/wizard/account_invoice_refund.py:161
1695+#, python-format
1696+msgid "Cannot %s an Intermission Voucher which is already reconciled, it should be unreconciled first."
1697+msgstr "Ne peut %s un Bon Intermission qui est déjà lettré, le lettrage du bon devrait d'abord être annulé."
1698+
1699+#. module: account
1700+#: code:addons/account/wizard/account_invoice_refund.py:0
1701+#, python-format
1702+msgid "modify"
1703+msgstr "modifier"
1704+
1705+#. module: account
1706+#: code:addons/account/wizard/account_invoice_refund.py:0
1707+#, python-format
1708+msgid "cancel"
1709+msgstr "annuler"
1710+
1711 #. modules: return_claim, msf_outgoing, stock_override, kit, msf_doc_import
1712 #: selection:assign.to.kit.line,integrity_status:0
1713 #: selection:kit.selection.line,integrity_status:0
1714@@ -97634,7 +97696,7 @@
1715 msgid "Document Date"
1716 msgstr "Date du Document"
1717
1718-#. modules: msf_order_date, purchase, msf_outgoing, stock_move_tracking, msf_partner, stock_override, purchase_override, msf_config_locations, sale, account_override, stock_batch_recall, specific_rules, msf_doc_import, stock, msf_supply_doc_export
1719+#. modules: msf_order_date, purchase, msf_outgoing, stock_move_tracking, msf_partner, stock_override, purchase_override, msf_config_locations, sale, account_override, stock_batch_recall, specific_rules, msf_doc_import, stock, msf_supply_doc_export, account
1720 #: field:account.account,has_partner_type_internal:0
1721 #: selection:stock.location.configuration.wizard,location_type:0
1722 #: selection:stock.remove.location.wizard,location_usage:0
1723@@ -97663,6 +97725,8 @@
1724 #: selection:stock.picking,fake_type:0
1725 #: selection:stock.picking,partner_type:0
1726 #: selection:stock.picking,partner_type_stock_picking:0
1727+#: selection:account.invoice,partner_type:0
1728+#: selection:account.invoice.line,partner_type:0
1729 #: code:addons/msf_supply_doc_export/wizard/supplier_performance_wizard.py:28
1730 #: field:supplier.performance.wizard,partner_type_internal:0
1731 #, python-format
1732@@ -101384,12 +101448,13 @@
1733 msgid "On "
1734 msgstr "Allumé "
1735
1736-#. modules: account, register_accounting
1737+#. modules: account, register_accounting, account_override
1738 #: selection:account.invoice,type:0
1739 #: selection:account.invoice.report,type:0
1740 #: model:process.process,name:account.process_process_invoiceprocess0
1741 #: selection:report.invoice.created,type:0
1742 #: selection:wizard.account.invoice,type:0
1743+#: selection:account.invoice.line,invoice_type:0
1744 msgid "Customer Invoice"
1745 msgstr "Bon client"
1746
1747@@ -106827,3 +106892,167 @@
1748 msgid "Stop report"
1749 msgstr "Arrêter le rapport"
1750
1751+#. module: account
1752+#: field:account.invoice.refund,is_intermission:0
1753+msgid "Wizard opened from an Intermission Voucher"
1754+msgstr "Assistant ouvert depuis un Bon Intermission"
1755+
1756+#. module: account_override
1757+#: code:addons/account_override/invoice.py:1739
1758+#, python-format
1759+msgid "This document has been generated via a Supply workflow or via synchronization. You can't add lines manually."
1760+msgstr "Ce document a été généré via un flux \"Supply\" ou via la synchronisation. Vous ne pouvez pas ajouter de lignes manuellement."
1761+
1762+#. module: account_override
1763+#: code:addons/account_override/invoice.py:1868
1764+#, python-format
1765+msgid "This document has been generated via a Supply workflow or via synchronization. Existing lines can't be deleted."
1766+msgstr "Ce document a été généré via un flux \"Supply\" ou via la synchronisation. Les lignes existantes ne peuvent pas être supprimées."
1767+
1768+#. module: account_override
1769+#: code:addons/account_override/invoice.py:848
1770+#, python-format
1771+msgid "Synchronized invoices are allowed only with Intermission and Intersection partners."
1772+msgstr "Les factures synchronisées ne sont autorisées qu'avec les partenaires Intermission et Intersection."
1773+
1774+#. module: account
1775+#: code:addons/account/invoice.py:683
1776+#, python-format
1777+msgid "Synchronization is allowed only with Intermission and Intersection partners."
1778+msgstr "La synchronisation n'est autorisée qu'avec les partenaires Intermission et Intersection."
1779+
1780+#. module: account_override
1781+#: view:account.invoice:0
1782+msgid "Are you sure you want to validate this invoice without synchronization?"
1783+msgstr "Etes-vous sûr de vouloir valider cette facture sans synchronisation ?"
1784+
1785+#. modules: account_override, account, register_accounting
1786+#: view:account.invoice:0
1787+msgid "Counterpart Invoice"
1788+msgstr "Facture de Contrepartie"
1789+
1790+#. modules: account_override, register_accounting
1791+#: field:account.invoice,counterpart_inv_number:0
1792+#: field:wizard.account.invoice,counterpart_inv_number:0
1793+msgid "Counterpart Invoice Number"
1794+msgstr "Numéro de la Facture de Contrepartie"
1795+
1796+#. modules: account_override, register_accounting
1797+#: field:account.invoice,counterpart_inv_status:0
1798+#: field:wizard.account.invoice,counterpart_inv_status:0
1799+msgid "Counterpart Invoice Status"
1800+msgstr "Etat de la Facture de Contrepartie"
1801+
1802+#. modules: account_override, register_accounting
1803+#: field:account.invoice,synced:0
1804+#: field:wizard.account.invoice,synced:0
1805+#: field:account.invoice.line,synced:0
1806+msgid "Synchronized"
1807+msgstr "Synchronisé"
1808+
1809+#. modules: account_override, register_accounting
1810+#: field:account.invoice,from_supply:0
1811+#: field:wizard.account.invoice,from_supply:0
1812+#: field:account.invoice.line,from_supply:0
1813+msgid "From Supply"
1814+msgstr "De la \"Supply\""
1815+
1816+#. module: account_override
1817+#: field:account.invoice.line,invoice_type:0
1818+msgid "Invoice Type"
1819+msgstr "Type de Facture"
1820+
1821+#. modules: account_override, register_accounting
1822+#: help:account.invoice,from_supply:0
1823+#: help:wizard.account.invoice,from_supply:0
1824+msgid "Internal field indicating whether the document is related to a Supply workflow"
1825+msgstr "Champ interne indiquant si le document est lié à un flux \"Supply\""
1826+
1827+#. module: account
1828+#: help:account.invoice,tax_line_ids:0
1829+msgid "Internal field for taxes always in read-only mode"
1830+msgstr "Champ interne pour les taxes toujours en mode lecture seule"
1831+
1832+#. module: account_override
1833+#: code:addons/account_override/account_invoice_sync.py:120
1834+#, python-format
1835+msgid "Account code %s not found."
1836+msgstr "Code comptable %s non trouvé."
1837+
1838+#. module: account_override
1839+#: code:addons/account_override/account_invoice_sync.py:189
1840+#, python-format
1841+msgid "Impossible to retrieve the currency."
1842+msgstr "Impossible de récupérer la devise."
1843+
1844+#. module: account_override
1845+#: code:addons/account_override/account_invoice_sync.py:117
1846+#, python-format
1847+msgid "Impossible to retrieve the account code at line level."
1848+msgstr "Impossible de récupérer le code comptable au niveau d'une ligne."
1849+
1850+#. module: account_override
1851+#: code:addons/account_override/account_invoice_sync.py:193
1852+#, python-format
1853+msgid "Currency %s not found or inactive."
1854+msgstr "Devise %s non trouvée ou inactive."
1855+
1856+#. module: account_override
1857+#: code:addons/account_override/account_invoice_sync.py:123
1858+#, python-format
1859+msgid "Error when retrieving the account at line level."
1860+msgstr "Erreur lors de la récupération du compte au niveau d'une ligne."
1861+
1862+#. module: account_override
1863+#: code:addons/account_override/account_invoice_sync.py:235
1864+#, python-format
1865+msgid "The Intermission counterpart account is missing in the Company form or is inactive."
1866+msgstr "Il manque le compte de Contrepartie Intermission dans le formulaire de la Société, ou bien ce compte est inactif."
1867+
1868+#. module: account_override
1869+#: code:addons/account_override/account_invoice_sync.py:186
1870+#, python-format
1871+msgid "Impossible to retrieve the journal type, or the journal type is incorrect."
1872+msgstr "Impossible de récupérer le type de journal, ou le type de journal est incorrect."
1873+
1874+#. module: account_override
1875+#: code:addons/account_override/account_invoice_sync.py:100
1876+#, python-format
1877+msgid "Impossible to retrieve the line description."
1878+msgstr "Impossible de récupérer la description de la ligne."
1879+
1880+#. module: account_override
1881+#: code:addons/account_override/account_invoice_sync.py:128
1882+#, python-format
1883+msgid "The account \"%s - %s\" is inactive."
1884+msgstr "Le compte \"%s - %s\" est inactif."
1885+
1886+#. module: account_override
1887+#: code:addons/account_override/account_invoice_sync.py:135
1888+#, python-format
1889+msgid "Unit of Measure %s not found."
1890+msgstr "Unité de Mesure %s non trouvée."
1891+
1892+#. module: account_override
1893+#: code:addons/account_override/account_invoice_sync.py:109
1894+#, python-format
1895+msgid "Product %s not found."
1896+msgstr "Produit %s non trouvé."
1897+
1898+#. module: account_override
1899+#: code:addons/account_override/account_invoice_sync.py:181
1900+#, python-format
1901+msgid "The partner %s doesn't exist or is inactive."
1902+msgstr "Le partenaire %s n'existe pas ou est inactif."
1903+
1904+#. module: account_override
1905+#: code:addons/account_override/account_invoice_sync.py:113
1906+#, python-format
1907+msgid "The product %s is inactive."
1908+msgstr "Le produit %s est inactif."
1909+
1910+#. module: account_override
1911+#: code:addons/account_override/account_invoice_sync.py:214
1912+#, python-format
1913+msgid "Account Payable not found or inactive for the partner %s."
1914+msgstr "Compte Fournisseur non trouvé ou inactif pour le partenaire %s."
1915
1916=== modified file 'bin/addons/msf_profile/msf_profile.py'
1917--- bin/addons/msf_profile/msf_profile.py 2019-06-20 14:28:17 +0000
1918+++ bin/addons/msf_profile/msf_profile.py 2019-07-15 07:18:58 +0000
1919@@ -69,6 +69,21 @@
1920 err_msg,
1921 )
1922
1923+ # UF14.0
1924+ def us_6076_set_inv_as_from_supply(self, cr, uid, *a, **b):
1925+ """
1926+ Set the new tag from_supply to True in the related account.invoices
1927+ """
1928+ update_inv = """
1929+ UPDATE account_invoice
1930+ SET from_supply = 't'
1931+ WHERE picking_id IS NOT NULL
1932+ OR id IN (SELECT DISTINCT (invoice_id) FROM shipment WHERE invoice_id IS NOT NULL);
1933+ """
1934+ cr.execute(update_inv)
1935+ self._logger.warn('Tag from_supply set to True in %s account.invoice(s).' % (cr.rowcount,))
1936+ return True
1937+
1938 # UF13.1
1939 def us_3413_align_in_partner_to_po(self,cr, uid, *a, **b):
1940 cr.execute("select p.name, p.id, po.partner_id, p.partner_id from stock_picking p, purchase_order po where p.type='in' and po.id = p.purchase_id and ( p.partner_id != po.partner_id or p.partner_id2 != po.partner_id) order by p.name")
1941
1942=== modified file 'bin/addons/msf_sync_data_server/data/sync_server.message_rule.csv'
1943--- bin/addons/msf_sync_data_server/data/sync_server.message_rule.csv 2019-02-16 16:57:55 +0000
1944+++ bin/addons/msf_sync_data_server/data/sync_server.message_rule.csv 2019-07-15 07:18:58 +0000
1945@@ -15,6 +15,10 @@
1946 msf_sync_data_server.validated_claim_create_claim,TRUE,TRUE,"['category_return_claim', 'creation_date_return_claim', 'description_return_claim', 'follow_up_return_claim', 'name', 'picking_id_return_claim/shipment_ref', 'po_id_return_claim/name', 'po_so_return_claim', 'goods_expected', 'processor_origin', 'state', 'type_return_claim', 'origin_claim', 'event_ids_return_claim/name', 'event_ids_return_claim/creation_date_claim_event', 'event_ids_return_claim/from_picking_wizard_claim_event', 'event_ids_return_claim/replacement_picking_expected_claim_event', 'event_ids_return_claim/type_claim_event', 'event_ids_return_claim/description_claim_event', 'product_line_ids_return_claim/price_unit_claim_product_line', 'product_line_ids_return_claim/qty_claim_product_line', 'product_line_ids_return_claim/product_id_claim_product_line/id', 'product_line_ids_return_claim/product_id_claim_product_line/name', 'product_line_ids_return_claim/price_currency_claim_product_line/id', 'product_line_ids_return_claim/price_currency_claim_product_line/name', 'product_line_ids_return_claim/uom_id_claim_product_line/id', 'product_line_ids_return_claim/uom_id_claim_product_line/name', 'product_line_ids_return_claim/type_check', 'product_line_ids_return_claim/expiry_date_claim_product_line']","[('state', '=', 'in_progress'), ('partner_id_return_claim.partner_type', 'in', ['internal', 'intermission', 'intersection']), ('type_return_claim', '=', 'supplier'), ('old_version', '=', False)]",partner_id_return_claim,MISSION,return.claim.validated_claim_create_claim,return.claim,Validated Claim creates Claim,28,,Valid
1947 msf_sync_data_server.origin_claim_close_claim,TRUE,TRUE,"['name', 'state', 'partner_id_return_claim/name', 'partner_id_return_claim/partner_type']","[('state', '=', 'done'), ('partner_id_return_claim.partner_type', '=', 'internal'), ('type_return_claim', '=', 'supplier'), ('old_version', '=', False)]",partner_id_return_claim,MISSION,return.claim.origin_claim_close_claim,return.claim,Original Closed Claim close counterpart Claim,29,,Valid
1948 msf_sync_data_server.goods_expecting_picking_from_claim_creates_fo,TRUE,TRUE,"['name', 'claim_name', 'purchase_id/name', 'purchase_id/delivery_requested_date','purchase_id/details', 'purchase_id/notes', 'purchase_id/categ', 'purchase_id/order_type', 'purchase_id/priority', 'purchase_id/loan_duration', 'purchase_id/analytic_distribution_id/id', 'purchase_id/is_a_counterpart', 'purchase_id/pricelist_id/name', 'purchase_id/stock_take_date', 'move_lines/product_id/name', 'move_lines/purchase_line_id/id', 'move_lines/purchase_line_id/sync_local_id', 'move_lines/name', 'move_lines/comment', 'move_lines/product_qty', 'move_lines/product_uom/name', 'move_lines/price_unit', 'move_lines/line_number']","['&', '&', ('state', '=', 'assigned'), ('partner_id.partner_type', '=', 'internal'), ('claim', '=', True), ('claim_name', '!=', False), '|', ('name', 'like', 'replacement'), ('name', 'like', 'missing')]",partner_id,MISSION,stock.picking.goods_expecting_picking_from_claim_creates_fo,stock.picking,IN-replacement/-missing created by Claim creates FO,30,,Valid
1949+msf_sync_data_server.create_account_invoice,TRUE,TRUE,"['number', 'document_date', 'date_invoice', 'journal_id/type', 'name', 'origin', 'from_supply', 'state', 'currency_id/name', 'invoice_line/name', 'invoice_line/quantity', 'invoice_line/uos_id/name', 'invoice_line/price_unit', 'invoice_line/discount', 'invoice_line/account_id/code', 'invoice_line/product_id/id', 'invoice_line/product_id/default_code']","[('type', '=', 'out_invoice'), ('is_debit_note', '=', False), ('state', '!=', 'draft'), ('number', '!=', False), ('synced', '=', True), ('partner_type', 'in', ('intermission', 'section'))]",partner_id,MISSION,account.invoice.create_invoice_from_sync,account.invoice,Account Invoice Creation,40,,Valid
1950+msf_sync_data_server.update_counterpart_inv_opened,TRUE,TRUE,"['number', 'state', 'counterpart_inv_number']","[('synced', '=', True), ('partner_type', 'in', ('intermission', 'section')), ('number', '!=', False), ('state', '=', 'open')]",partner_id,MISSION,account.invoice.update_counterpart_inv_opened,account.invoice,Account Invoice Open State Update,42,,Valid
1951+msf_sync_data_server.update_counterpart_inv_paid,TRUE,TRUE,"['number', 'state', 'counterpart_inv_number']","[('synced', '=', True), ('partner_type', 'in', ('intermission', 'section')), ('number', '!=', False), ('state', '=', 'paid')]",partner_id,MISSION,account.invoice.update_counterpart_inv_paid,account.invoice,Account Invoice Paid State Update,43,,Valid
1952+msf_sync_data_server.update_counterpart_inv_cancelled,TRUE,TRUE,"['number', 'state', 'counterpart_inv_number']","[('synced', '=', True), ('partner_type', 'in', ('intermission', 'section')), ('number', '!=', False), ('state', '=', 'cancel')]",partner_id,MISSION,account.invoice.update_counterpart_inv_cancelled,account.invoice,Account Invoice Cancel State Update,44,,Valid
1953 msf_sync_data_server.create_batch_object,FALSE,TRUE,"['name', 'xmlid_name', 'prefix', 'product_id/id', 'product_id/default_code', 'partner_id/id', 'date', 'ref','life_date','sequence_id','type']","[('name', '=', False)]",partner_id,MISSION,stock.picking.create_batch_number,stock.production.lot,Create Batch Object,1001,,Valid
1954 msf_sync_data_server.create_asset_object,TRUE,TRUE,"['name', 'xmlid_name', 'arrival_date', 'asset_type_id/id', 'partner_id/id', 'brand', 'comment', 'description', 'hq_ref', 'international_po', 'invo_certif_depreciation', 'invo_currency/id', 'invo_date', 'invo_donator_code', 'invo_num', 'invo_supplier', 'invo_value', 'local_ref', 'model', 'orig_mission_code', 'product_id/id', 'product_id/default_code', 'project_po', 'receipt_place', 'serial_nb', 'type', 'year']","[('name', '=', False)]",partner_id,MISSION,stock.picking.create_asset,product.asset,Create Asset Object,1002,,Valid
1955 msf_sync_data_server.reset_ref_by_recovery_mode,TRUE,TRUE,['name'],"[('name', '=', False)]",partner_id,MISSION,sale.order.reset_ref_by_recovery_mode,sale.order,Reset Due to Recovery,1003,,Valid
1956
1957=== modified file 'bin/addons/purchase/purchase_workflow.py'
1958--- bin/addons/purchase/purchase_workflow.py 2019-05-14 08:00:10 +0000
1959+++ bin/addons/purchase/purchase_workflow.py 2019-07-15 07:18:58 +0000
1960@@ -502,15 +502,15 @@
1961
1962 def check_po_tax(self, cr, uid, ids, context=None):
1963 """
1964- Prevents from validating a PO with taxes when using an Intermission partner
1965+ Prevents from validating a PO with taxes when using an Intermission or Intersection partner
1966 """
1967 if context is None:
1968 context = {}
1969 if isinstance(ids, (int, long)):
1970 ids = [ids]
1971 for po_line in self.browse(cr, uid, ids, fields_to_fetch=['order_id', 'taxes_id'], context=context):
1972- if po_line.taxes_id and po_line.order_id.partner_type == 'intermission':
1973- raise osv.except_osv(_('Error'), _("You can't use taxes with an intermission partner."))
1974+ if po_line.taxes_id and po_line.order_id.partner_type in ('intermission', 'section'):
1975+ raise osv.except_osv(_('Error'), _("Taxes are forbidden with Intermission and Intersection partners."))
1976
1977 def check_origin_for_validation(self, cr, uid, ids, context=None):
1978 if not context:
1979
1980=== modified file 'bin/addons/register_accounting/account_invoice_view.xml'
1981--- bin/addons/register_accounting/account_invoice_view.xml 2018-05-15 08:55:26 +0000
1982+++ bin/addons/register_accounting/account_invoice_view.xml 2019-07-15 07:18:58 +0000
1983@@ -316,11 +316,15 @@
1984 </field>
1985 <xpath expr="/form/notebook/page[@string='Invoice']/field[@name='invoice_line']" position="before" >
1986 <group name="import" string=" Import Lines " colspan="4" col="4" attrs="{'invisible':[('state', '!=', 'draft')]}">
1987- <button name="wizard_import_si_line" string="Import lines" icon="gtk-dnd" type="object" attrs="{'invisible':[('state', '!=', 'draft')]}"/>
1988+ <button name="wizard_import_si_line" string="Import lines" icon="gtk-dnd" type="object"
1989+ attrs="{'invisible': [('state', '!=', 'draft')],
1990+ 'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
1991 </group>
1992 </xpath>
1993 <field name="journal_id" position="replace">
1994- <field name="journal_id" domain="[('is_current_instance','=',True), ('type', '=', context.get('journal_type'))]"/>
1995+ <field name="journal_id"
1996+ domain="[('is_current_instance','=',True), ('type', '=', context.get('journal_type'))]"
1997+ attrs="{'readonly': [('type', '=', 'in_invoice'), ('synced', '=', True)]}"/>
1998 <field name="vat_ok" invisible="1" />
1999 </field>
2000 <button name="action_cancel_draft" position="replace"/>
2001@@ -348,6 +352,14 @@
2002 </tree>
2003 </field>
2004 </page>
2005+ <!-- display the Counterpart Invoice tab in SI, hide it in SR -->
2006+ <page string="Counterpart Invoice" attrs="{'invisible': [('type', '!=', 'in_invoice')]}">
2007+ <field name="from_supply" invisible="1"/> <!-- make the field exportable -->
2008+ <field name="synced" readonly="1"/> <!-- SI can never be ticked as "Synced" manually -->
2009+ <newline/>
2010+ <field name="counterpart_inv_number"/>
2011+ <field name="counterpart_inv_status"/>
2012+ </page>
2013 </page>
2014
2015 <xpath expr="/form//field[@name='tax_line']" position="attributes">
2016
2017=== modified file 'bin/addons/sale/sale_workflow.py'
2018--- bin/addons/sale/sale_workflow.py 2019-05-14 08:00:10 +0000
2019+++ bin/addons/sale/sale_workflow.py 2019-07-15 07:18:58 +0000
2020@@ -529,15 +529,15 @@
2021
2022 def check_fo_tax(self, cr, uid, ids, context=None):
2023 """
2024- Prevents from validating a FO with taxes when using an Intermission partner
2025+ Prevents from validating a FO with taxes when using an Intermission or Intersection partner
2026 """
2027 if context is None:
2028 context = {}
2029 if isinstance(ids, (int, long)):
2030 ids = [ids]
2031 for fo_line in self.browse(cr, uid, ids, fields_to_fetch=['order_id', 'tax_id'], context=context):
2032- if fo_line.tax_id and fo_line.order_id.partner_type == 'intermission':
2033- raise osv.except_osv(_('Error'), _("You can't use taxes with an intermission partner."))
2034+ if fo_line.tax_id and fo_line.order_id.partner_type in ('intermission', 'section'):
2035+ raise osv.except_osv(_('Error'), _("Taxes are forbidden with Intermission and Intersection partners."))
2036
2037 def action_validate(self, cr, uid, ids, context=None):
2038 '''
2039
2040=== modified file 'bin/addons/stock/stock.py'
2041--- bin/addons/stock/stock.py 2019-05-10 14:40:57 +0000
2042+++ bin/addons/stock/stock.py 2019-07-15 07:18:58 +0000
2043@@ -1228,6 +1228,7 @@
2044 'company_id': picking.company_id.id,
2045 'user_id':uid,
2046 'picking_id': picking.id,
2047+ 'from_supply': True,
2048 }
2049 if picking.sale_id:
2050 if not partner.property_account_position.id:
2051@@ -1321,6 +1322,13 @@
2052 if origin_ivi:
2053 invoice_vals.update({'origin': origin_ivi})
2054
2055+ # Add "synced" tag for STV and IVO created from Supply flow
2056+ out_invoice = inv_type == 'out_invoice'
2057+ is_stv = out_invoice and not di and not inkind_donation and not intermission
2058+ is_ivo = out_invoice and not debit_note and not inkind_donation and intermission
2059+ if is_stv or is_ivo:
2060+ invoice_vals.update({'synced': True, })
2061+
2062 # Update Payment terms and due date for the Supplier Invoices and Refunds
2063 if is_si or inv_type == 'in_refund':
2064 si_payment_term = self._get_payment_term(cr, uid, picking)
2065
2066=== modified file 'bin/addons/stock_override/stock.py'
2067--- bin/addons/stock_override/stock.py 2019-05-10 14:31:04 +0000
2068+++ bin/addons/stock_override/stock.py 2019-07-15 07:18:58 +0000
2069@@ -942,6 +942,11 @@
2070 if sp.type == 'out' and sp.partner_id.partner_type == 'external' and invoice_type != 'in_refund':
2071 res = False
2072
2073+ # Move in on an intermission or intersection partner should not create an IVI / SI (generation of Donations shouldn't be blocked)
2074+ if sp.type == 'in' and sp.purchase_id and sp.purchase_id.order_type not in ('donation_st', 'donation_exp') \
2075+ and sp.partner_id and sp.partner_id.partner_type in ('intermission', 'section') and invoice_type == 'in_invoice':
2076+ res = False
2077+
2078 return res
2079
2080 def _create_invoice(self, cr, uid, stock_picking):
2081
2082=== modified file 'bin/addons/sync_so/picking.py'
2083--- bin/addons/sync_so/picking.py 2018-06-08 14:22:21 +0000
2084+++ bin/addons/sync_so/picking.py 2019-07-15 07:18:58 +0000
2085@@ -316,10 +316,7 @@
2086 if shipment:
2087 shipment_ref = shipment['name'] # shipment made
2088 else:
2089- #UFTP-332: Check if name is really an OUT, because DPO could have PICk but no SHIP nor OUT --> do not link this PICK to IN
2090 shipment_ref = pick_dict.get('name', False) # the case of OUT
2091- if shipment_ref and 'OUT' not in shipment_ref:
2092- shipment_ref = False
2093 if not po_id and pick_dict.get('sale_id') and pick_dict.get('sale_id', {}).get('claim_name_goods_return'):
2094 po_sync_name = pick_dict.get('sale_id', {}).get('client_order_ref')
2095 if po_sync_name:
2096@@ -756,6 +753,9 @@
2097 so_po_common = self.pool.get('so.po.common')
2098 so_po_common.create_invalid_recovery_message(cr, uid, source, in_name, context)
2099 return "Recovery: the reference to " + in_name + " at " + source + " will be set to void."
2100+ elif 'PICK' in out_doc_name:
2101+ return "Msg ignored"
2102+
2103 if message:
2104 self._logger.info(message)
2105 return message

Subscribers

People subscribed via source and target branches