diff options
100 files changed, 1640 insertions, 698 deletions
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 50760c25ff9..accec89538a 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -772,12 +772,12 @@ class AccountInvoice(models.Model): msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.') raise RedirectWarning(msg, action.id, _('Go to the configuration panel')) - if type in ('out_invoice', 'out_refund'): - account_id = rec_account.id - payment_term_id = p.property_payment_term_id.id - else: + if type in ('in_invoice', 'in_refund'): account_id = pay_account.id payment_term_id = p.property_supplier_payment_term_id.id + else: + account_id = rec_account.id + payment_term_id = p.property_payment_term_id.id delivery_partner_id = self.get_delivery_partner_id() fiscal_position = self.env['account.fiscal.position'].get_fiscal_position(self.partner_id.id, delivery_id=delivery_partner_id) diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py index 6df0ffb1274..bc660b494b3 100644 --- a/addons/account/report/account_aged_partner_balance.py +++ b/addons/account/report/account_aged_partner_balance.py @@ -28,7 +28,7 @@ class ReportAgedPartnerBalance(models.AbstractModel): ctx = self._context periods = {} date_from = fields.Date.from_string(date_from) - start = date_from - relativedelta(days=1) + start = date_from for i in range(5)[::-1]: stop = start - relativedelta(days=period_length) period_name = str((5-(i+1)) * period_length + 1) + '-' + str((5-i) * period_length) diff --git a/addons/account/static/src/scss/account_reconciliation.scss b/addons/account/static/src/scss/account_reconciliation.scss index 0eda5d43fc0..1d94d0cbcb7 100644 --- a/addons/account/static/src/scss/account_reconciliation.scss +++ b/addons/account/static/src/scss/account_reconciliation.scss @@ -292,6 +292,10 @@ overflow: auto; border: 1px solid #ddd; border-radius: 0 0 5px 5px; + > div.quick_add > .o_reconcile_models { + max-height: 54px; + overflow: auto; + } } .quick_add { margin-bottom: 7px; @@ -360,4 +364,4 @@ } } } -}
\ No newline at end of file +} diff --git a/addons/account/wizard/account_invoice_send_views.xml b/addons/account/wizard/account_invoice_send_views.xml index 90ecfadf793..6052c1438d9 100644 --- a/addons/account/wizard/account_invoice_send_views.xml +++ b/addons/account/wizard/account_invoice_send_views.xml @@ -83,7 +83,6 @@ view_type="form" context="{ 'default_template_id': ref('account.email_template_edi_invoice'), - 'default_is_print': False, 'mark_invoice_as_sent': True, }" groups="account.group_account_invoice"/> diff --git a/addons/account_payment/controllers/payment.py b/addons/account_payment/controllers/payment.py index 496a158563e..8ac01bd398d 100644 --- a/addons/account_payment/controllers/payment.py +++ b/addons/account_payment/controllers/payment.py @@ -77,11 +77,11 @@ class PaymentPortal(http.Controller): vals = { 'payment_token_id': token.id, 'type': 'server2server', - 'return_url': success_url, + 'return_url': _build_url_w_params(success_url, params), } tx = invoice_sudo._create_payment_transaction(vals) PaymentProcessing.add_payment_transaction(tx) params['success'] = 'pay_invoice' - return request.redirect(_build_url_w_params(success_url, params)) + return request.redirect('/payment/process') diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index a3c20bd00de..a5d7dae73a6 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -1205,6 +1205,10 @@ class Lead(models.Model): if msg_dict.get('priority') in dict(crm_stage.AVAILABLE_PRIORITIES): defaults['priority'] = msg_dict.get('priority') defaults.update(custom_values) + + # assign right company + if 'company_id' not in defaults and 'team_id' in defaults: + defaults['company_id'] = self.env['crm.team'].browse(defaults['team_id']).company_id.id return super(Lead, self).message_new(msg_dict, custom_values=defaults) def _message_post_after_hook(self, message, *args, **kwargs): diff --git a/addons/crm/tests/test_new_lead_notification.py b/addons/crm/tests/test_new_lead_notification.py index dedcd02449a..16228fcc55c 100644 --- a/addons/crm/tests/test_new_lead_notification.py +++ b/addons/crm/tests/test_new_lead_notification.py @@ -41,3 +41,95 @@ class NewLeadNotification(TestCrmCases): # The user should have a new unread message lead_user = lead.sudo(self.crm_salesman) self.assertTrue(lead_user.message_needaction) + + def test_new_lead_from_email_multicompany(self): + company0 = self.env.user.company_id + company1 = self.env['res.company'].create({'name': 'new_company'}) + + self.env.user.write({ + 'company_ids': [(4, company0.id, False), (4, company1.id, False)], + }) + + crm_team_model = self.env['ir.model'].search([('model', '=', 'crm.team')]) + crm_lead_model = self.env['ir.model'].search([('model', '=', 'crm.lead')]) + self.env["ir.config_parameter"].sudo().set_param("mail.catchall.domain", 'aqualung.com') + + crm_team0 = self.env['crm.team'].create({ + 'name': 'crm team 0', + 'company_id': company0.id, + }) + crm_team1 = self.env['crm.team'].create({ + 'name': 'crm team 1', + 'company_id': company1.id, + }) + + mail_alias0 = self.env['mail.alias'].create({ + 'alias_name': 'sale_team_0', + 'alias_model_id': crm_lead_model.id, + 'alias_parent_model_id': crm_team_model.id, + 'alias_parent_thread_id': crm_team0.id, + 'alias_defaults': "{'type': 'opportunity', 'team_id': %s}" % crm_team0.id, + }) + mail_alias1 = self.env['mail.alias'].create({ + 'alias_name': 'sale_team_1', + 'alias_model_id': crm_lead_model.id, + 'alias_parent_model_id': crm_team_model.id, + 'alias_parent_thread_id': crm_team1.id, + 'alias_defaults': "{'type': 'opportunity', 'team_id': %s}" % crm_team1.id, + }) + + crm_team0.write({'alias_id': mail_alias0.id}) + crm_team1.write({'alias_id': mail_alias1.id}) + + new_message0 = """MIME-Version: 1.0 +Date: Thu, 27 Dec 2018 16:27:45 +0100 +Message-ID: blablabla0 +Subject: sale team 0 in company 0 +From: A client <client_a@someprovider.com> +To: sale_team_0@aqualung.com +Content-Type: multipart/alternative; boundary="000000000000a47519057e029630" + +--000000000000a47519057e029630 +Content-Type: text/plain; charset="UTF-8" + + +--000000000000a47519057e029630 +Content-Type: text/html; charset="UTF-8" +Content-Transfer-Encoding: quoted-printable + +<div>A good message</div> + +--000000000000a47519057e029630-- +""" + + new_message1 = """MIME-Version: 1.0 +Date: Thu, 27 Dec 2018 16:27:45 +0100 +Message-ID: blablabla1 +Subject: sale team 1 in company 1 +From: B client <client_b@someprovider.com> +To: sale_team_1@aqualung.com +Content-Type: multipart/alternative; boundary="000000000000a47519057e029630" + +--000000000000a47519057e029630 +Content-Type: text/plain; charset="UTF-8" + + +--000000000000a47519057e029630 +Content-Type: text/html; charset="UTF-8" +Content-Transfer-Encoding: quoted-printable + +<div>A good message bis</div> + +--000000000000a47519057e029630-- +""" + crm_lead0_id = self.env['mail.thread'].message_process('crm.lead', new_message0) + crm_lead1_id = self.env['mail.thread'].message_process('crm.lead', new_message1) + + crm_lead0 = self.env['crm.lead'].browse(crm_lead0_id) + crm_lead1 = self.env['crm.lead'].browse(crm_lead1_id) + + self.assertEqual(crm_lead0.team_id, crm_team0) + self.assertEqual(crm_lead1.team_id, crm_team1) + + self.assertEqual(crm_lead0.company_id, company0) + self.assertEqual(crm_lead1.company_id, company1) diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py index b749f615293..1b15093de03 100644 --- a/addons/event_sale/models/sale_order.py +++ b/addons/event_sale/models/sale_order.py @@ -12,8 +12,16 @@ class SaleOrder(models.Model): for so in self: # confirm registration if it was free (otherwise it will be confirmed once invoice fully paid) so.order_line._update_registrations(confirm=so.amount_total == 0, cancel_to_draft=False) + return res + + @api.multi + def action_confirm(self): + res = super(SaleOrder, self).action_confirm() + for so in self: if any(so.order_line.filtered(lambda line: line.event_id)): - return self.env['ir.actions.act_window'].with_context(default_sale_order_id=so.id).for_xml_id('event_sale', 'action_sale_order_event_registration') + return self.env['ir.actions.act_window'] \ + .with_context(default_sale_order_id=so.id) \ + .for_xml_id('event_sale', 'action_sale_order_event_registration') return res diff --git a/addons/event_sale/wizard/event_edit_registration.xml b/addons/event_sale/wizard/event_edit_registration.xml index 92a69107ba7..25422c2947a 100644 --- a/addons/event_sale/wizard/event_edit_registration.xml +++ b/addons/event_sale/wizard/event_edit_registration.xml @@ -12,7 +12,7 @@ <tree string="Registration" editable="top" create="false" delete="false"> <field name="event_id" readonly='1' force_save="1"/> <field name="registration_id" readonly='1' force_save="1"/> - <field name="event_ticket_id" domain="[('event_id', '=', event_id)]" readonly='1'/> + <field name="event_ticket_id" domain="[('event_id', '=', event_id)]" readonly='1' force_save="1"/> <field name="name"/> <field name="email"/> <field name="phone"/> diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index 24062454dca..37e12bd0c86 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -427,7 +427,7 @@ class HolidaysRequest(models.Model): employee = self.env['hr.employee'].browse(employee_id) return employee.get_work_days_data(date_from, date_to)['days'] - return self.env.user.company_id.resource_calendar_id.get_work_hours_count() / HOURS_PER_DAY + return self.env.user.company_id.resource_calendar_id.get_work_hours_count(date_from, date_to) / HOURS_PER_DAY #################################################### # ORM Overrides methods diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py index c542ea14909..a0e9ba65e8d 100644 --- a/addons/hr_holidays/models/hr_leave_type.py +++ b/addons/hr_holidays/models/hr_leave_type.py @@ -8,6 +8,7 @@ import logging from odoo import api, fields, models from odoo.exceptions import ValidationError +from odoo.osv import expression from odoo.tools.translate import _ from odoo.tools.float_utils import float_round @@ -207,9 +208,18 @@ class HolidaysType(models.Model): @api.multi def _compute_group_days_allocation(self): + domain = [ + ('holiday_status_id', 'in', self.ids), + ('holiday_type', '!=', 'employee'), + ('state', '=', 'validate'), + ] + domain2 = [ + '|', + ('date_from', '>=', fields.Datetime.to_string(datetime.datetime.now().replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0))), + ('date_from', '=', False), + ] grouped_res = self.env['hr.leave.allocation'].read_group( - [('holiday_status_id', 'in', self.ids), ('holiday_type', '!=', 'employee'), ('state', '=', 'validate'), - ('date_from', '>=', fields.Datetime.to_string(datetime.datetime.now().replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0)))], + expression.AND([domain, domain2]), ['holiday_status_id', 'number_of_days'], ['holiday_status_id'], ) @@ -270,11 +280,16 @@ class HolidaysType(models.Model): def action_see_days_allocated(self): self.ensure_one() action = self.env.ref('hr_holidays.hr_leave_allocation_action_all').read()[0] - action['domain'] = [ + domain = [ + ('holiday_status_id', 'in', self.ids), ('holiday_type', '!=', 'employee'), - ('holiday_status_id', '=', self.ids[0]), - ('date_from', '>=', fields.Datetime.to_string(datetime.datetime.now().replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0))) ] + domain2 = [ + '|', + ('date_from', '>=', fields.Datetime.to_string(datetime.datetime.now().replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0))), + ('date_from', '=', False), + ] + action['domain'] = expression.AND([domain, domain2]) action['context'] = { 'default_holiday_type': 'department', 'default_holiday_status_id': self.ids[0], diff --git a/addons/hr_org_chart/static/src/xml/hr_org_chart.xml b/addons/hr_org_chart/static/src/xml/hr_org_chart.xml index fa5db5f32a8..893e0394f90 100644 --- a/addons/hr_org_chart/static/src/xml/hr_org_chart.xml +++ b/addons/hr_org_chart/static/src/xml/hr_org_chart.xml @@ -104,6 +104,7 @@ <div class="o_media_left"> <a href="#" t-att-data-employee-id="self.id" + t-att-data-employee-name="self.name" class="o_org_chart_show_more text-center o_employee_sub_redirect">…</a> </div> </div> diff --git a/addons/hr_timesheet/views/project_views.xml b/addons/hr_timesheet/views/project_views.xml index 5c51e6eb4d5..3296f9c891a 100644 --- a/addons/hr_timesheet/views/project_views.xml +++ b/addons/hr_timesheet/views/project_views.xml @@ -81,7 +81,7 @@ You can not log timesheets on this project since is linked to an inactive analytic account. Please change it, or reactivate the current one to timesheet on the project. </div> </group> - <field name="timesheet_ids" attrs="{'invisible': [('analytic_account_active', '=', False)]}" context="{'default_project_id': project_id, 'default_name':''}"> + <field name="timesheet_ids" mode="tree,kanban" attrs="{'invisible': [('analytic_account_active', '=', False)]}" context="{'default_project_id': project_id, 'default_name':''}"> <tree editable="bottom" string="Timesheet Activities" default_order="date"> <field name="date"/> <field name="user_id" invisible="1"/> @@ -90,6 +90,50 @@ <field name="unit_amount" widget="timesheet_uom"/> <field name="project_id" invisible="1"/> </tree> + <kanban class="o_kanban_mobile"> + <field name="date"/> + <field name="user_id"/> + <field name="employee_id"/> + <field name="name"/> + <field name="unit_amount"/> + <field name="project_id"/> + <templates> + <t t-name="kanban-box"> + <div t-attf-class="oe_kanban_card oe_kanban_global_click"> + <div class="row"> + <div class="col-xs-6"> + <strong><span><t t-esc="record.employee_id.value"/></span></strong> + </div> + <div class="col-xs-6 pull-right text-right"> + <strong><t t-esc="record.date.value"/></strong> + </div> + </div> + <div class="row"> + <div class="col-xs-6 text-muted"> + <span><t t-esc="record.name.value"/></span> + </div> + <div class="col-xs-6"> + <span class="pull-right text-right"> + <field name="unit_amount" widget="float_time"/> + </span> + </div> + </div> + </div> + </t> + </templates> + </kanban> + <form string="Timesheet Activities"> + <sheet> + <group> + <field name="date"/> + <field name="user_id" invisible="1"/> + <field name="employee_id" required="1"/> + <field name="name"/> + <field name="unit_amount" string="Duration" widget="float_time"/> + <field name="project_id" invisible="1"/> + </group> + </sheet> + </form> </field> <group attrs="{'invisible': [('analytic_account_active', '=', False)]}"> <group class="oe_subtotal_footer oe_right" name="project_hours"> diff --git a/addons/hw_drivers/controllers/driver.py b/addons/hw_drivers/controllers/driver.py index 7e2a775e4b7..3772dfa32f9 100755 --- a/addons/hw_drivers/controllers/driver.py +++ b/addons/hw_drivers/controllers/driver.py @@ -344,15 +344,19 @@ def send_iot_box_device(send_printer): printerList = {} if send_printer: printers = subprocess.check_output("sudo lpinfo -lv", shell=True).decode('utf-8').split('Device') + printers_installed = '' + try: + printers_installed = subprocess.check_output("sudo lpstat -a", shell=True).decode('utf-8') + except: + pass for printer in printers: printerTab = printer.split('\n') - if printer and printerTab[4].split('=')[1] != ' ': + if printer and printerTab[4].split('=')[1] != ' ' or 'lpd://' in printerTab[0]: device_connection = printerTab[1].split('= ')[1] model = '' for device_id in printerTab[4].split('= ')[1].split(';'): if any(x in device_id for x in ['MDL','MODEL']): model = device_id.split(':')[1] - name = printerTab[2].split('= ')[1] serial = re.sub('[^a-zA-Z0-9 ]+', '', model).replace(' ','_') identifier = '' if device_connection == 'direct': @@ -361,6 +365,9 @@ def send_iot_box_device(send_printer): socketIP = printerTab[0].split('://')[1] macprinter = subprocess.check_output("arp -a " + socketIP + " |awk NR==1'{print $4}'", shell=True).decode('utf-8').split('\n')[0] identifier = macprinter # macPRINTER + elif device_connection == 'network' and 'lpd' in printerTab[0]: + identifier = re.sub('[^a-zA-Z0-9 ]+', '', printerTab[0].split('://')[1]) + model = printerTab[2].split('= ')[1] elif device_connection == 'network' and 'dnssd' in printerTab[0]: hostname_printer = subprocess.check_output("ippfind -n \"" + model + "\" | awk \'{split($0,a,\"/\"); print a[3]}\' | awk \'{split($0,b,\":\"); print b[1]}\'", shell=True).decode('utf-8').split('\n')[0] if hostname_printer: @@ -375,15 +382,16 @@ def send_iot_box_device(send_printer): 'type': 'printer' } - # install these printers - try: - ppd = subprocess.check_output("sudo lpinfo -m |grep '" + model + "'", shell=True).decode('utf-8').split('\n') - if len(ppd) > 2: + if identifier not in printers_installed: + # install these printers + try: + ppd = subprocess.check_output("sudo lpinfo -m |grep '" + model + "'", shell=True).decode('utf-8').split('\n') + if len(ppd) > 2: + subprocess.call("sudo lpadmin -p '" + identifier + "' -E -v '" + printerTab[0].split('= ')[1] + "'", shell=True) + else: + subprocess.call("sudo lpadmin -p '" + identifier + "' -E -v '" + printerTab[0].split('= ')[1] + "' -m '" + ppd[0].split(' ')[0] + "'", shell=True) + except: subprocess.call("sudo lpadmin -p '" + identifier + "' -E -v '" + printerTab[0].split('= ')[1] + "'", shell=True) - else: - subprocess.call("sudo lpadmin -p '" + identifier + "' -E -v '" + printerTab[0].split('= ')[1] + "' -m '" + ppd[0].split(' ')[0] + "'", shell=True) - except: - subprocess.call("sudo lpadmin -p '" + identifier + "' -E -v '" + printerTab[0].split('= ')[1] + "'", shell=True) subprocess.call('> /tmp/printers', shell=True) for printer in printerList: subprocess.call('echo "' + printerList[printer]['name'] + '" >> /tmp/printers', shell=True) @@ -409,14 +417,16 @@ def send_iot_box_device(send_printer): data['devices'] = devicesList data_json = json.dumps(data).encode('utf8') headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} - http = urllib3.PoolManager() + urllib3.disable_warnings() + http = urllib3.PoolManager(cert_reqs='CERT_NONE') req = False try: req = http.request('POST', url, body=data_json, headers=headers) - except: + except Exception as e: _logger.warning('Could not reach configured server') + _logger.error('A error encountered : %s ' % e) diff --git a/addons/hw_posbox_homepage/controllers/main.py b/addons/hw_posbox_homepage/controllers/main.py index 3a21ba8cdae..f3de5a0d138 100644 --- a/addons/hw_posbox_homepage/controllers/main.py +++ b/addons/hw_posbox_homepage/controllers/main.py @@ -119,12 +119,15 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home): f = open('/tmp/devices', 'r') for line in f: url = 'http://' + self.get_ip_iotbox() + ':8069/hw_drivers/driverdetails/' + line.split('|')[0] - http = urllib3.PoolManager() + urllib3.disable_warnings() + http = urllib3.PoolManager(cert_reqs='CERT_NONE') value = '' try: req = http.request('GET', url) - except: + except Exception as e: req = '' + _logger.warning('Could not reach configured server') + _logger.error('A error encountered : %s ' % e) if req: value = req.data.decode('utf-8') @@ -193,15 +196,18 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home): #response = requests.get(url, auth=(username, db_uuid.split('\n')[0]), stream=True) server = self.get_server_status() if server: - pm = urllib3.PoolManager() + urllib3.disable_warnings() + pm = urllib3.PoolManager(cert_reqs='CERT_NONE') resp = False server = server + '/iot/get_drivers' try: resp = pm.request('POST', server, fields={'mac': mac}) - except: + except Exception as e: + req = '' _logger.warning('Could not reach configured server') + _logger.error('A error encountered : %s ' % e) if resp and resp.data: zip_file = zipfile.ZipFile(io.BytesIO(resp.data)) zip_file.extractall("/home/pi/odoo/addons/hw_drivers/drivers") diff --git a/addons/hw_posbox_homepage/views/homepage.html b/addons/hw_posbox_homepage/views/homepage.html index e952e5620f4..71c0f26c565 100644 --- a/addons/hw_posbox_homepage/views/homepage.html +++ b/addons/hw_posbox_homepage/views/homepage.html @@ -90,7 +90,7 @@ </tr> <tr> <td class="heading">Version</td> - <td>V18.10 <a class="btn btn-sm float-right" href='/hw_proxy/upgrade/'>update</a></td> + <td>V18.12 <a class="btn btn-sm float-right" href='/hw_proxy/upgrade/'>update</a></td> </tr> <tr> <td class="heading">IP Address</td> @@ -136,5 +136,6 @@ <div style="margin: 20px auto 10px auto;" class="text-center"> <a class="btn" href='/point_of_sale/display'>POS Display</a> <a class="btn" style="margin-left: 10px;" href='/remote_connect'>Remote Debug</a> + <a target="_blank" class="btn" style="margin-left: 10px;" href="http://{{ ip }}:631">Printers server</a> </div> {% endblock %} diff --git a/addons/im_livechat/models/mail_channel.py b/addons/im_livechat/models/mail_channel.py index bb38a314fb0..8b81501db20 100644 --- a/addons/im_livechat/models/mail_channel.py +++ b/addons/im_livechat/models/mail_channel.py @@ -42,6 +42,13 @@ class MailChannel(models.Model): 'Livechat Operator ID is required for a channel of type livechat.')] @api.multi + def _compute_is_chat(self): + super(MailChannel, self)._compute_is_chat() + for record in self: + if record.channel_type == 'livechat': + record.is_chat = True + + @api.multi def _channel_message_notifications(self, message): """ When a anonymous user create a mail.channel, the operator is not notify (to avoid massive polling when clicking on livechat button). So when the anonymous person is sending its FIRST message, the channel header diff --git a/addons/l10n_be/i18n_extra/l10n_be.pot b/addons/l10n_be/i18n_extra/l10n_be.pot index 0697b138641..cf3158c7467 100644 --- a/addons/l10n_be/i18n_extra/l10n_be.pot +++ b/addons/l10n_be/i18n_extra/l10n_be.pot @@ -4,10 +4,10 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 11.0alpha1\n" +"Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-03-06 12:52+0000\n" -"PO-Revision-Date: 2017-03-06 12:52+0000\n" +"POT-Creation-Date: 2019-01-17 08:59+0000\n" +"PO-Revision-Date: 2019-01-17 08:59+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -16,950 +16,779 @@ msgstr "" "Plural-Forms: \n" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-00 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-00 msgid "0% Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-00-G #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-00-G msgid "0% Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-00-CC #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-00-CC msgid "0% Cocont." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-00-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-00-CC msgid "0% Cocont. - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-00-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-00-CC msgid "0% Cocont. M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-00-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-00-CC msgid "0% Cocont. S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-00-EU #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-00-EU msgid "0% EU - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-00-EU-G #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-00-EU-G msgid "0% EU - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-00-EU -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-00-EU-L #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-00-EU #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-00-EU-L msgid "0% EU M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-00-EU-S -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-00-EU-S #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-00-EU-S #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-00-EU-S msgid "0% EU S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-00-EU-T #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-00-EU-T msgid "0% EU T." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-00 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-00 msgid "0% M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-00-ROW #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-00-ROW msgid "0% Non EU" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-00-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-00-ROW-CC msgid "0% Non EU - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-00-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-00-ROW-CC msgid "0% Non EU M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-00-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-00-ROW-CC msgid "0% Non EU S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-00-S -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-00-S #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-00-S #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-00-S msgid "0% S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-12-L #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-12-L msgid "12%" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12 msgid "12% Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-G #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-G msgid "12% Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12-CC msgid "12% Cocont. - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12-CC-C1 msgid "12% Cocont. Déductible - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12-CC-C1 msgid "12% Cocont. Déductible M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-CC-C1 msgid "12% Cocont. Déductible S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12-CC msgid "12% Cocont. M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12-CC-C2 msgid "12% Cocont. Récupérable - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12-CC-C2 msgid "12% Cocont. Récupérable M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-CC-C2 msgid "12% Cocont. Récupérable S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-CC msgid "12% Cocont. S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12-EU #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12-EU msgid "12% EU - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-EU-G #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-EU-G msgid "12% EU - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12-EU-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12-EU-C1 msgid "12% EU Déductible - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-EU-G-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-EU-G-C1 msgid "12% EU Déductible - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12-EU-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12-EU-C1 msgid "12% EU Déductible M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-EU-S-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-EU-S-C1 msgid "12% EU Déductible S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12-EU #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12-EU msgid "12% EU M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12-EU-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12-EU-C2 msgid "12% EU Récupérable - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12-EU-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12-EU-C2 msgid "12% EU Récupérable M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-EU-S-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-EU-S-C2 msgid "12% EU Récupérable S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-EU-S #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-EU-S msgid "12% EU S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12 msgid "12% M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12-ROW-CC msgid "12% Non EU - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12-ROW-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12-ROW-CC-C1 msgid "12% Non EU Déductible - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12-ROW-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12-ROW-CC-C1 msgid "12% Non EU Déductible M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-ROW-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-ROW-CC-C1 msgid "12% Non EU Déductible S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12-ROW-CC msgid "12% Non EU M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-12-ROW-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-12-ROW-CC-C2 msgid "12% Non EU Récupérable - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-12-ROW-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-12-ROW-CC-C2 msgid "12% Non EU Récupérable M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-ROW-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-ROW-CC-C2 msgid "12% Non EU Récupérable S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-ROW-CC msgid "12% Non EU S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-S -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-12-S #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-S #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-12-S msgid "12% S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-21-L #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-21-L msgid "21%" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21 msgid "21% Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-G #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-G msgid "21% Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-CC msgid "21% Cocont .S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21-CC msgid "21% Cocont. - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21-CC-C1 msgid "21% Cocont. Déductible - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21-CC-C1 msgid "21% Cocont. Déductible M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-CC-C1 msgid "21% Cocont. Déductible S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21-CC msgid "21% Cocont. M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21-CC-C2 msgid "21% Cocont. Récupérable - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21-CC-C2 msgid "21% Cocont. Récupérable M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-CC-C2 msgid "21% Cocont. Récupérable S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21-EU #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21-EU msgid "21% EU - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-EU-G #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-EU-G msgid "21% EU - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21-EU-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21-EU-C1 msgid "21% EU Déductible - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-EU-G-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-EU-G-C1 msgid "21% EU Déductible - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21-EU-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21-EU-C1 msgid "21% EU Déductible M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-EU-S-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-EU-S-C1 msgid "21% EU Déductible S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21-EU #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21-EU msgid "21% EU M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21-EU-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21-EU-C2 msgid "21% EU Récupérable - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-EU-G-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-EU-G-C2 msgid "21% EU Récupérable - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21-EU-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21-EU-C2 msgid "21% EU Récupérable M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-EU-S-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-EU-S-C2 msgid "21% EU Récupérable S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-EU-S #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-EU-S msgid "21% EU S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21 msgid "21% M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21-ROW-CC msgid "21% Non EU - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21-ROW-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21-ROW-CC-C1 msgid "21% Non EU Déductible - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21-ROW-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21-ROW-CC-C1 msgid "21% Non EU Déductible M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-ROW-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-ROW-CC-C1 msgid "21% Non EU Déductible S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21-ROW-CC msgid "21% Non EU M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-21-ROW-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-21-ROW-CC-C2 msgid "21% Non EU Récupérable - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-21-ROW-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-21-ROW-CC-C2 msgid "21% Non EU Récupérable M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-ROW-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-ROW-CC-C2 msgid "21% Non EU Récupérable S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-ROW-CC msgid "21% Non EU S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-21-S -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-21-S #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-21-S #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-21-S msgid "21% S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_TVA-21-inclus-dans-prix #: model:account.tax.template,name:l10n_be.attn_TVA-21-inclus-dans-prix msgid "21% S. TTC" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-CAR-EXC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-CAR-EXC-C2 msgid "50% Déductible - Frais de voiture (Prix Excl.)" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-CAR-EXC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-CAR-EXC-C1 msgid "50% Non Déductible - Frais de voiture" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-CAR-EXC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-CAR-EXC msgid "50% Non Déductible - Frais de voiture (Prix Excl.)" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06 msgid "6% Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-G #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-G msgid "6% Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06-CC msgid "6% Cocont. - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06-CC-C1 msgid "6% Cocont. Déductible - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-CC-C1 msgid "6% Cocont. Déductible M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-CC-C1 msgid "6% Cocont. Déductible S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-CC msgid "6% Cocont. M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06-CC-C2 msgid "6% Cocont. Récupérable - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-CC-C2 msgid "6% Cocont. Récupérable M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-CC-C2 msgid "6% Cocont. Récupérable S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-CC msgid "6% Cocont. S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06-EU #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06-EU msgid "6% EU - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-EU-G #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-EU-G msgid "6% EU - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06-EU-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06-EU-C1 msgid "6% EU Déductible - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-EU-G-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-EU-G-C1 msgid "6% EU Déductible - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06-EU-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-EU-C1 msgid "6% EU Déductible M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-EU-S-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-EU-S-C1 msgid "6% EU Déductible S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06-EU #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-EU msgid "6% EU M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06-EU-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06-EU-C2 msgid "6% EU Récupérable - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-EU-G-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-EU-G-C2 msgid "6% EU Récupérable - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06-EU-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-EU-C2 msgid "6% EU Récupérable M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-EU-S-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-EU-S-C2 msgid "6% EU Récupérable S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-EU-S #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-EU-S msgid "6% EU S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06 msgid "6% M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06-ROW-CC msgid "6% Non EU - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06-ROW-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06-ROW-CC-C1 msgid "6% Non EU Déductible - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06-ROW-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-ROW-CC-C1 msgid "6% Non EU Déductible M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-ROW-CC-C1 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-ROW-CC-C1 msgid "6% Non EU Déductible S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-ROW-CC msgid "6% Non EU M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V83-06-ROW-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V83-06-ROW-CC-C2 msgid "6% Non EU Récupérable - Biens d'investissement" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V81-06-ROW-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-ROW-CC-C2 msgid "6% Non EU Récupérable M." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-ROW-CC-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-ROW-CC-C2 msgid "6% Non EU Récupérable S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-ROW-CC #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-ROW-CC msgid "6% Non EU S." msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-06-S -#: model:account.tax,name:l10n_be.1_attn_VAT-OUT-06-S #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-06-S #: model:account.tax.template,name:l10n_be.attn_VAT-OUT-06-S msgid "6% S." msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a601 #: model:account.account.template,name:l10n_be.a601 msgid "Achats de fournitures" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a604 #: model:account.account.template,name:l10n_be.a604 msgid "Achats de marchandises" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a600 #: model:account.account.template,name:l10n_be.a600 msgid "Achats de matières premières" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a602 #: model:account.account.template,name:l10n_be.a602 msgid "Achats de services, travaux et études" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a446 #: model:account.account.template,name:l10n_be.a446 msgid "Acomptes reçus" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a176 #: model:account.account.template,name:l10n_be.a176 msgid "Acomptes reçus sur commandes" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a406 #: model:account.account.template,name:l10n_be.a406 msgid "Acomptes versés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a360 #: model:account.account.template,name:l10n_be.a360 msgid "Acomptes versés sur achats pour stocks" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4550 #: model:account.account.template,name:l10n_be.a4550 msgid "Administrateurs, gérants et commissaires" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2119 #: model:account.account.template,name:l10n_be.a2119 msgid "Amortissements sur concessions, brevets, etc..." msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2690 #: model:account.account.template,name:l10n_be.a2690 msgid "Amortissements sur frais d'aménagement des locaux pris en location" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2009 #: model:account.account.template,name:l10n_be.a2009 msgid "Amortissements sur frais de constitution et d'augmentation de capital" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2109 #: model:account.account.template,name:l10n_be.a2109 msgid "Amortissements sur frais de recherche et de mise au point" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a24190 #: model:account.account.template,name:l10n_be.a24190 msgid "Amortissements sur matériel automobile" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a24090 #: model:account.account.template,name:l10n_be.a24090 msgid "Amortissements sur mobilier" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2529 #: model:account.account.template,name:l10n_be.a2529 msgid "Amortissements sur mobilier et matériel roulant en leasing" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a614 #: model:account.account.template,name:l10n_be.a614 msgid "Annonces, publicité, propagande et documentation" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a3100 #: model:account.account.template,name:l10n_be.a3100 msgid "Approvisionements et fournitures" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4160 #: model:account.account.template,name:l10n_be.a4160 msgid "Associés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a45932 #: model:account.account.template,name:l10n_be.a45932 msgid "Assurance groupe " msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a45930 #: model:account.account.template,name:l10n_be.a45930 msgid "Assurance loi" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a45931 #: model:account.account.template,name:l10n_be.a45931 msgid "Assurance salaire garanti " msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a45933 #: model:account.account.template,name:l10n_be.a45933 msgid "Assurances individuelles" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a41671 #: model:account.account.template,name:l10n_be.a41671 msgid "Autres créances" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a174 -#: model:account.account,name:l10n_be.1_a424 #: model:account.account.template,name:l10n_be.a174 #: model:account.account.template,name:l10n_be.a424 msgid "Autres emprunts" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4125 #: model:account.account.template,name:l10n_be.a4125 msgid "Autres impôts belges" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a49102 -#: model:account.account,name:l10n_be.1_a740 #: model:account.account.template,name:l10n_be.a49102 #: model:account.account.template,name:l10n_be.a740 msgid "Autres produits d'exploitation" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4538 #: model:account.account.template,name:l10n_be.a4538 msgid "Autres précomptes retenus" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4161 #: model:account.account.template,name:l10n_be.a4161 msgid "Avances et prêts au personnel" msgstr "" #. module: l10n_be -#: model:account.chart.template,name:l10n_be.l10nbe_chart_template -msgid "Belgian PCMN" -msgstr "" - -#. module: l10n_be -#: model:ir.ui.menu,name:l10n_be.account_reports_be_statements_menu -msgid "Belgium Statements" -msgstr "" - -#. module: l10n_be #: model:account.account.tag,name:l10n_be.tax_tag_81_not_deductible msgid "Belgium VAT Form: Tax not deductible (grid 81)" msgstr "" @@ -1130,854 +959,707 @@ msgid "Belgium VAT Form: grid 88" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a140 #: model:account.account.template,name:l10n_be.a140 msgid "Bénéfice reporté" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a790 #: model:account.account.template,name:l10n_be.a790 msgid "Bénéfice reporté de l'exercice précédent" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a693 #: model:account.account.template,name:l10n_be.a693 msgid "Bénéfice à reporter" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4594 #: model:account.account.template,name:l10n_be.a4594 msgid "Caisse d'assurances sociales pour travailleurs indépendants" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a24105 #: model:account.account.template,name:l10n_be.a24105 msgid "Camions" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a1000 #: model:account.account.template,name:l10n_be.a1000 msgid "Capital" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a418 #: model:account.account.template,name:l10n_be.a418 msgid "Cautionnements versés en numéraires" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6600 #: model:account.account.template,name:l10n_be.a6600 msgid "Charges exceptionelles" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6101 #: model:account.account.template,name:l10n_be.a6101 msgid "Charges locatives" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a492 #: model:account.account.template,name:l10n_be.a492 msgid "Charges à imputer" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a490 #: model:account.account.template,name:l10n_be.a490 msgid "Charges à reporter" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a540 #: model:account.account.template,name:l10n_be.a540 msgid "Chèques à encaisser" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4000 #: model:account.account.template,name:l10n_be.a4000 msgid "Clients" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a370 #: model:account.account.template,name:l10n_be.a370 msgid "Commandes en cours d'exécution" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a49101 #: model:account.account.template,name:l10n_be.a49101 msgid "Commissions à obtenir" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a448 #: model:account.account.template,name:l10n_be.a448 msgid "Compensations fournisseurs" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4112 -#: model:account.account,name:l10n_be.1_a4512 #: model:account.account.template,name:l10n_be.a4112 #: model:account.account.template,name:l10n_be.a4512 msgid "Compte courant administration T.V.A." msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4163 #: model:account.account.template,name:l10n_be.a4163 msgid "Compte courant des administrateurs et gérants" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4162 #: model:account.account.template,name:l10n_be.a4162 msgid "Compte courant des associés en S.P.R.L." msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4990 #: model:account.account.template,name:l10n_be.a4990 msgid "Compte d'attente" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2110 #: model:account.account.template,name:l10n_be.a2110 msgid "Concessions, brevets, licences, savoir-faire, marques, etc..." msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2501 #: model:account.account.template,name:l10n_be.a2501 msgid "Constructions" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a621 #: model:account.account.template,name:l10n_be.a621 msgid "Cotisations patronales d'assurances sociales" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a29000 #: model:account.account.template,name:l10n_be.a29000 msgid "Créances commerciales" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a407 -#: model:account.account,name:l10n_be.1_a417 #: model:account.account.template,name:l10n_be.a407 #: model:account.account.template,name:l10n_be.a417 msgid "Créances douteuses" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4164 #: model:account.account.template,name:l10n_be.a4164 msgid "Créances sur sociétés apparentées" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a1720 #: model:account.account.template,name:l10n_be.a1720 msgid "Dettes de location-financement de biens immobiliers" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a1721 #: model:account.account.template,name:l10n_be.a1721 msgid "Dettes de location-financement de biens mobiliers" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a17300 #: model:account.account.template,name:l10n_be.a17300 msgid "Dettes en compte \\ Banque A" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4597 #: model:account.account.template,name:l10n_be.a4597 msgid "Dettes et provisions sociales diverses" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4500 #: model:account.account.template,name:l10n_be.a4500 msgid "Dettes fiscales estimées" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a1722 #: model:account.account.template,name:l10n_be.a1722 msgid "Dettes sur droits réels sur immeubles" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a654 -#: model:account.account,name:l10n_be.1_a754 #: model:account.account.template,name:l10n_be.a654 #: model:account.account.template,name:l10n_be.a754 msgid "Différences de change" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4551 -#: model:account.account,name:l10n_be.1_a4560 #: model:account.account.template,name:l10n_be.a4551 #: model:account.account.template,name:l10n_be.a4560 msgid "Direction" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6302 #: model:account.account.template,name:l10n_be.a6302 msgid "Dotations aux amortissements sur immobilisations corporelles" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6301 #: model:account.account.template,name:l10n_be.a6301 msgid "Dotations aux amortissements sur immobilisations incorporelles" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a755 #: model:account.account.template,name:l10n_be.a755 msgid "Ecarts de conversion des devises" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a175111 #: model:account.account.template,name:l10n_be.a175111 msgid "Effets à payer \\ Fournisseurs C.E.E." msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a175110 #: model:account.account.template,name:l10n_be.a175110 msgid "Effets à payer \\ Fournisseurs belges" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a175112 #: model:account.account.template,name:l10n_be.a175112 msgid "Effets à payer \\ Fournisseurs importation" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4166 #: model:account.account.template,name:l10n_be.a4166 msgid "Emballages et matériel à rendre" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4552 -#: model:account.account,name:l10n_be.1_a4561 #: model:account.account.template,name:l10n_be.a4552 #: model:account.account.template,name:l10n_be.a4561 msgid "Employés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a3200 #: model:account.account.template,name:l10n_be.a3200 msgid "En cours de fabrication" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a611 #: model:account.account.template,name:l10n_be.a611 msgid "Entretien et réparation" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a653 #: model:account.account.template,name:l10n_be.a653 msgid "Escomptes clients" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a757 #: model:account.account.template,name:l10n_be.a757 msgid "Escomptes obtenus" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4230 #: model:account.account.template,name:l10n_be.a4230 msgid "Etablissements de crédit" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a430 #: model:account.account.template,name:l10n_be.a430 msgid "Etablissements de crédit. Emprunts en compte à terme fixe" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a444 #: model:account.account.template,name:l10n_be.a444 msgid "Factures à recevoir" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4220 #: model:account.account.template,name:l10n_be.a4220 msgid "Financement de biens immobiliers" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4221 #: model:account.account.template,name:l10n_be.a4221 msgid "Financement de biens mobiliers" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a440 #: model:account.account.template,name:l10n_be.a440 msgid "Fournisseurs" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a612 #: model:account.account.template,name:l10n_be.a612 msgid "Fournitures" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a260 #: model:account.account.template,name:l10n_be.a260 msgid "Frais d'aménagements de locaux pris en location" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a656 #: model:account.account.template,name:l10n_be.a656 msgid "Frais de banques, de chèques postaux" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2000 #: model:account.account.template,name:l10n_be.a2000 msgid "Frais de constitution et d'augmentation de capital" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2100 #: model:account.account.template,name:l10n_be.a2100 msgid "Frais de recherche et de mise au point" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a3500 #: model:account.account.template,name:l10n_be.a3500 msgid "Immeubles destinés à la vente" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2800 #: model:account.account.template,name:l10n_be.a2800 msgid "Immobilisations Financières" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4120 #: model:account.account.template,name:l10n_be.a4120 msgid "Impôts belges sur le résultat" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6700 #: model:account.account.template,name:l10n_be.a6700 msgid "Impôts et précomptes dus ou versés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4128 #: model:account.account.template,name:l10n_be.a4128 msgid "Impôts étrangers" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6100 +#: model:account.account.template,name:l10n_be.l10nbe_chart_template_liquidity_transfer +msgid "Liquidity Transfer" +msgstr "" + +#. module: l10n_be #: model:account.account.template,name:l10n_be.a6100 msgid "Loyers divers" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a3400 #: model:account.account.template,name:l10n_be.a3400 msgid "Marchandises" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a300 #: model:account.account.template,name:l10n_be.a300 msgid "Matières premières" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a24011 #: model:account.account.template,name:l10n_be.a24011 msgid "Matériel de bureau de bâtiments administratifs et commerciaux" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a24010 #: model:account.account.template,name:l10n_be.a24010 msgid "Matériel de bureau de bâtiments industriels" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2521 #: model:account.account.template,name:l10n_be.a2521 msgid "Matériel roulant" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2520 #: model:account.account.template,name:l10n_be.a2520 msgid "Mobilier" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a24001 #: model:account.account.template,name:l10n_be.a24001 msgid "Mobilier des bâtiments administratifs et commerciaux" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a24000 #: model:account.account.template,name:l10n_be.a24000 msgid "Mobilier des bâtiments industriels" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a151 #: model:account.account.template,name:l10n_be.a151 msgid "Montants transférés aux résultats" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4541 #: model:account.account.template,name:l10n_be.a4541 msgid "ONSS. 1er trimestre" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4542 #: model:account.account.template,name:l10n_be.a4542 msgid "ONSS. 2ème trimestre" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4543 #: model:account.account.template,name:l10n_be.a4543 msgid "ONSS. 3ème trimestre" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4544 #: model:account.account.template,name:l10n_be.a4544 msgid "ONSS. 4ème trimestre" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4540 #: model:account.account.template,name:l10n_be.a4540 msgid "ONSS. Arriérés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4553 -#: model:account.account,name:l10n_be.1_a4562 #: model:account.account.template,name:l10n_be.a4553 #: model:account.account.template,name:l10n_be.a4562 msgid "Ouvriers" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a141 #: model:account.account.template,name:l10n_be.a141 msgid "Perte reportée" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a690 #: model:account.account.template,name:l10n_be.a690 msgid "Perte reportée de l'exercice précédent" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a793 #: model:account.account.template,name:l10n_be.a793 msgid "Perte à reporter" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2118 #: model:account.account.template,name:l10n_be.a2118 msgid "Plus-values actées sur concessions, brevets, etc..." msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2108 #: model:account.account.template,name:l10n_be.a2108 msgid "Plus-values actées sur frais de recherche et de mise au point" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2528 #: model:account.account.template,name:l10n_be.a2528 msgid "Plus-values actées sur mobilier et matériel roulant en leasing" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a752 #: model:account.account.template,name:l10n_be.a752 msgid "Plus-values sur réalisations d'actifs circulants" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7051 #: model:account.account.template,name:l10n_be.a7051 msgid "Prestations de services dans les pays membres de la C.E.E." msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7050 #: model:account.account.template,name:l10n_be.a7050 msgid "Prestations de services en Belgique" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7052 #: model:account.account.template,name:l10n_be.a7052 msgid "Prestations de services en vue de l'exportation" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a11 #: model:account.account.template,name:l10n_be.a11 msgid "Primes d'émission" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4930 #: model:account.account.template,name:l10n_be.a4930 msgid "Produits d'exploitation à reporter" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a751 #: model:account.account.template,name:l10n_be.a751 msgid "Produits des actifs circulants" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a756 #: model:account.account.template,name:l10n_be.a756 msgid "Produits des autres créances" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7600 #: model:account.account.template,name:l10n_be.a7600 msgid "Produits exceptionnels" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a3300 #: model:account.account.template,name:l10n_be.a3300 msgid "Produits finis" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a404 -#: model:account.account,name:l10n_be.1_a414 #: model:account.account.template,name:l10n_be.a404 #: model:account.account.template,name:l10n_be.a414 msgid "Produits à recevoir" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4590 #: model:account.account.template,name:l10n_be.a4590 msgid "Provision pour gratifications de fin d'année" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a1600 #: model:account.account.template,name:l10n_be.a1600 msgid "Provisions pour risques et charges" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4533 #: model:account.account.template,name:l10n_be.a4533 msgid "Précompte mobilier retenu sur intérêts payés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4530 #: model:account.account.template,name:l10n_be.a4530 msgid "Précompte professionnel retenu sur rémunérations" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a791 #: model:account.account.template,name:l10n_be.a791 msgid "Prélèvement sur le capital et les primes d'émission" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a780 #: model:account.account.template,name:l10n_be.a780 msgid "Prélèvements sur les impôts différés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a789 #: model:account.account.template,name:l10n_be.a789 msgid "Prélèvements sur les réserves immunisées" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a706 #: model:account.account.template,name:l10n_be.a706 msgid "Pénalités et dédits obtenus par l'entreprise" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4168 #: model:account.account.template,name:l10n_be.a4168 msgid "Rabais, ristournes, remises à obtenir et autres avoirs non encore reçus" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7500 #: model:account.account.template,name:l10n_be.a7500 msgid "Revenus des actions" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7502 #: model:account.account.template,name:l10n_be.a7502 msgid "Revenus des créances à plus d'un an" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7501 #: model:account.account.template,name:l10n_be.a7501 msgid "Revenus des obligations" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a49100 #: model:account.account.template,name:l10n_be.a49100 msgid "Ristournes, rabais à obtenir" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a329 -#: model:account.account,name:l10n_be.1_a419 #: model:account.account.template,name:l10n_be.a329 #: model:account.account.template,name:l10n_be.a419 msgid "Réductions de valeur actées" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6320 #: model:account.account.template,name:l10n_be.a6320 msgid "Réductions de valeur sur commandes en cours d'exécution" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6310 #: model:account.account.template,name:l10n_be.a6310 msgid "Réductions de valeur sur stocks" msgstr "" #. module: l10n_be -#: model:account.fiscal.position,name:l10n_be.1_fiscal_position_template_4 #: model:account.fiscal.position.template,name:l10n_be.fiscal_position_template_4 msgid "Régime Cocontractant" msgstr "" #. module: l10n_be -#: model:account.fiscal.position,name:l10n_be.1_fiscal_position_template_2 #: model:account.fiscal.position.template,name:l10n_be.fiscal_position_template_2 msgid "Régime Extra-Communautaire" msgstr "" #. module: l10n_be -#: model:account.fiscal.position,name:l10n_be.1_fiscal_position_template_3 #: model:account.fiscal.position.template,name:l10n_be.fiscal_position_template_3 msgid "Régime Intra-Communautaire" msgstr "" #. module: l10n_be -#: model:account.fiscal.position,name:l10n_be.1_fiscal_position_template_1 #: model:account.fiscal.position.template,name:l10n_be.fiscal_position_template_1 msgid "Régime National" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V61 -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V61 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V61 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V61 msgid "Régularisation en faveur de l'état" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V62 -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V62 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V62 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V62 msgid "Régularisation en faveur du déclarant" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7710 #: model:account.account.template,name:l10n_be.a7710 msgid "Régularisations d'impôts dus ou versés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7711 #: model:account.account.template,name:l10n_be.a7711 msgid "Régularisations d'impôts estimés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6200 #: model:account.account.template,name:l10n_be.a6200 msgid "Rémunérations: Administrateurs ou gérants" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6204 #: model:account.account.template,name:l10n_be.a6204 msgid "Rémunérations: Autres membres du personnel" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6202 #: model:account.account.template,name:l10n_be.a6202 msgid "Rémunérations: Employés" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6203 #: model:account.account.template,name:l10n_be.a6203 msgid "Rémunérations: Ouvriers" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a6201 #: model:account.account.template,name:l10n_be.a6201 msgid "Rémunérations: Personnel de direction" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a130 #: model:account.account.template,name:l10n_be.a130 msgid "Réserve légale" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a132 #: model:account.account.template,name:l10n_be.a132 msgid "Réserves immunisées" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a613 #: model:account.account.template,name:l10n_be.a613 msgid "Rétributions de tiers" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a603 #: model:account.account.template,name:l10n_be.a603 msgid "Sous-traitances générales" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a753 #: model:account.account.template,name:l10n_be.a753 msgid "Subsides en capital et en intérêts" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a150 #: model:account.account.template,name:l10n_be.a150 msgid "Subsides obtenus" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a41670 #: model:account.account.template,name:l10n_be.a41670 msgid "Subsides à recevoir" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a411059 #: model:account.account.template,name:l10n_be.a411059 msgid "T.V.A Déductible" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a64012 #: model:account.account.template,name:l10n_be.a64012 msgid "T.V.A. non déductible" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a451054 #: model:account.account.template,name:l10n_be.a451054 msgid "T.V.A. à payer" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a451056 #: model:account.account.template,name:l10n_be.a451056 msgid "T.V.A. à payer - Cocontractant" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a451057 #: model:account.account.template,name:l10n_be.a451057 msgid "T.V.A. à payer - Import" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a451055 #: model:account.account.template,name:l10n_be.a451055 msgid "T.V.A. à payer - Intra-communautaire" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-00 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-00-G -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-00-S -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-00 -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-00-L -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-00-S +#: model:account.tax.group,name:l10n_be.tax_group_tva_0 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-00 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-00-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-00-S @@ -1988,10 +1670,6 @@ msgid "TVA 0%" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-00-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-00-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-00-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-00-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-00-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-00-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-00-CC @@ -2000,13 +1678,6 @@ msgid "TVA 0% Cocont." msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-00-EU -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-00-EU-G -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-00-EU-S -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-00-EU -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-00-EU-L -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-00-EU-S -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-00-EU-T #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-00-EU #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-00-EU-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-00-EU-S @@ -2018,10 +1689,11 @@ msgid "TVA 0% EU" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-00-ROW-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-00-ROW-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-00-ROW-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-00-ROW +#: model:account.tax.group,name:l10n_be.tax_group_tva_0_eu +msgid "TVA 0% EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-00-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-00-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-00-ROW-CC @@ -2030,12 +1702,7 @@ msgid "TVA 0% Non EU" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-G -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-S -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12 -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-12-L -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-12-S +#: model:account.tax.group,name:l10n_be.tax_group_tva_12 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-S @@ -2046,9 +1713,7 @@ msgid "TVA 12%" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12-CC +#: model:account.tax.group,name:l10n_be.tax_group_tva_12_cocont #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-12-CC @@ -2056,9 +1721,6 @@ msgid "TVA 12% Cocont." msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-12-CC-C1 @@ -2066,9 +1728,6 @@ msgid "TVA 12% Cocont. Déductible" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-12-CC-C2 @@ -2076,10 +1735,6 @@ msgid "TVA 12% Cocont. Récupérable" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12-EU -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-EU-G -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-EU-S -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12-EU #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-EU #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-EU-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-EU-S @@ -2088,10 +1743,6 @@ msgid "TVA 12% EU" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12-EU-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-EU-G-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-EU-S-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12-EU-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-EU-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-EU-G-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-EU-S-C1 @@ -2100,10 +1751,6 @@ msgid "TVA 12% EU Déductible" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12-EU-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-EU-G-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-EU-S-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12-EU-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-EU-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-EU-G-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-EU-S-C2 @@ -2112,15 +1759,16 @@ msgid "TVA 12% EU Récupérable" msgstr "" #. module: l10n_be -#: model:account.tax,name:l10n_be.1_attn_VAT-IN-V82-12-EU-G-C2 #: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-12-EU-G-C2 msgid "TVA 12% EU Récupérable - Biens divers" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12-ROW-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-ROW-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12-ROW-CC +#: model:account.tax.group,name:l10n_be.tax_group_tva_12_eu +msgid "TVA 12% EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-12-ROW-CC @@ -2128,9 +1776,6 @@ msgid "TVA 12% Non EU" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12-ROW-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-ROW-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12-ROW-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-ROW-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-ROW-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-12-ROW-CC-C1 @@ -2138,9 +1783,6 @@ msgid "TVA 12% Non EU Déductible" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-12-ROW-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-12-ROW-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-12-ROW-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-ROW-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-ROW-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-12-ROW-CC-C2 @@ -2148,12 +1790,12 @@ msgid "TVA 12% Non EU Récupérable" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-G -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-S -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21 -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-21-L -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-21-S +#: model:account.tax.group,name:l10n_be.tax_group_tva_12_non_eu +msgid "TVA 12% Non EU." +msgstr "" + +#. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_21 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-S @@ -2164,9 +1806,7 @@ msgid "TVA 21%" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21-CC +#: model:account.tax.group,name:l10n_be.tax_group_tva_21_cocont #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-21-CC @@ -2174,9 +1814,6 @@ msgid "TVA 21% Cocont." msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-21-CC-C1 @@ -2184,9 +1821,6 @@ msgid "TVA 21% Cocont. Déductible" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-21-CC-C2 @@ -2194,10 +1828,6 @@ msgid "TVA 21% Cocont. Récupérable" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21-EU -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-EU-G -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-EU-S -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21-EU #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-EU #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-EU-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-EU-S @@ -2206,10 +1836,6 @@ msgid "TVA 21% EU" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21-EU-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-EU-G-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-EU-S-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21-EU-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-EU-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-EU-G-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-EU-S-C1 @@ -2218,10 +1844,6 @@ msgid "TVA 21% EU Déductible" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21-EU-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-EU-G-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-EU-S-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21-EU-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-EU-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-EU-G-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-EU-S-C2 @@ -2230,9 +1852,11 @@ msgid "TVA 21% EU Récupérable" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21-ROW-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-ROW-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21-ROW-CC +#: model:account.tax.group,name:l10n_be.tax_group_tva_21_eu +msgid "TVA 21% EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-21-ROW-CC @@ -2240,9 +1864,6 @@ msgid "TVA 21% Non EU" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21-ROW-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-ROW-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21-ROW-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-ROW-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-ROW-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-21-ROW-CC-C1 @@ -2250,9 +1871,6 @@ msgid "TVA 21% Non EU Déductible" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-21-ROW-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-21-ROW-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-21-ROW-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-ROW-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-ROW-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-21-ROW-CC-C2 @@ -2260,30 +1878,32 @@ msgid "TVA 21% Non EU Récupérable" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_TVA-21-inclus-dans-prix +#: model:account.tax.group,name:l10n_be.tax_group_tva_21_non_eu +msgid "TVA 21% Non EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_TVA-21-inclus-dans-prix msgid "TVA 21% TTC" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-CAR-EXC-C2 +#: model:account.tax.group,name:l10n_be.tax_group_tva_21_50_deductible +msgid "TVA 21%, 50% Deductible." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-CAR-EXC-C2 msgid "TVA 50% Déductible - Frais de voiture (Prix Excl.)" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-CAR-EXC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-CAR-EXC msgid "TVA 50% Non Déductible - Frais de voiture (Prix Excl.)" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-G -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-S -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06 -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-06-L -#: model:account.tax,description:l10n_be.1_attn_VAT-OUT-06-S +#: model:account.tax.group,name:l10n_be.tax_group_tva_6 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-S @@ -2294,9 +1914,7 @@ msgid "TVA 6%" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06-CC +#: model:account.tax.group,name:l10n_be.tax_group_tva_6_cocont #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-06-CC @@ -2304,9 +1922,6 @@ msgid "TVA 6% Cocont." msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-06-CC-C1 @@ -2314,9 +1929,6 @@ msgid "TVA 6% Cocont. Déductible" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-06-CC-C2 @@ -2324,10 +1936,6 @@ msgid "TVA 6% Cocont. Récupérable" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06-EU -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-EU-G -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-EU-S -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06-EU #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-EU #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-EU-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-EU-S @@ -2336,10 +1944,6 @@ msgid "TVA 6% EU" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06-EU-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-EU-G-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-EU-S-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06-EU-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-EU-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-EU-G-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-EU-S-C1 @@ -2348,10 +1952,6 @@ msgid "TVA 6% EU Déductible" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06-EU-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-EU-G-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-EU-S-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06-EU-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-EU-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-EU-G-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-EU-S-C2 @@ -2360,9 +1960,11 @@ msgid "TVA 6% EU Récupérable" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06-ROW-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-ROW-CC -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06-ROW-CC +#: model:account.tax.group,name:l10n_be.tax_group_tva_6_eu +msgid "TVA 6% EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-06-ROW-CC @@ -2370,9 +1972,6 @@ msgid "TVA 6% Non EU" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06-ROW-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-ROW-CC-C1 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06-ROW-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-ROW-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-ROW-CC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-06-ROW-CC-C1 @@ -2380,9 +1979,6 @@ msgid "TVA 6% Non EU Déductible" msgstr "" #. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V81-06-ROW-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-06-ROW-CC-C2 -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V83-06-ROW-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-ROW-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-ROW-CC-C2 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-06-ROW-CC-C2 @@ -2390,74 +1986,61 @@ msgid "TVA 6% Non EU Récupérable" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a4118 +#: model:account.tax.group,name:l10n_be.tax_group_tva_6_non_eu +msgid "TVA 6% Non EU." +msgstr "" + +#. module: l10n_be #: model:account.account.template,name:l10n_be.a4118 msgid "Taxe d'égalisation due" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a64000 #: model:account.account.template,name:l10n_be.a64000 msgid "Taxes sur autos et camions" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a2500 #: model:account.account.template,name:l10n_be.a2500 msgid "Terrains" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_trans -#: model:account.account.template,name:l10n_be.trans -msgid "Transferts" -msgstr "" - -#. module: l10n_be -#: model:account.tax,description:l10n_be.1_attn_VAT-IN-V82-CAR-EXC-C1 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-CAR-EXC-C1 msgid "VAT 50% Non Déductible - Frais de voiture" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7001 #: model:account.account.template,name:l10n_be.a7001 msgid "Ventes dans les pays membres de la C.E.E. (marchandises)" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7011 #: model:account.account.template,name:l10n_be.a7011 msgid "Ventes dans les pays membres de la C.E.E. (produits finis)" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7000 #: model:account.account.template,name:l10n_be.a7000 msgid "Ventes en Belgique (marchandises)" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7010 #: model:account.account.template,name:l10n_be.a7010 msgid "Ventes en Belgique (produits finis)" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7002 #: model:account.account.template,name:l10n_be.a7002 msgid "Ventes à l'exportation (marchandises)" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a7012 #: model:account.account.template,name:l10n_be.a7012 msgid "Ventes à l'exportation (produits finis)" msgstr "" #. module: l10n_be -#: model:account.account,name:l10n_be.1_a24100 #: model:account.account.template,name:l10n_be.a24100 msgid "Voitures" msgstr "" - diff --git a/addons/l10n_be/i18n_extra/nl.po b/addons/l10n_be/i18n_extra/nl.po index 2a4b067ee33..f031378df09 100644 --- a/addons/l10n_be/i18n_extra/nl.po +++ b/addons/l10n_be/i18n_extra/nl.po @@ -4,9 +4,9 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 9.0\n" +"Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-03-06 12:52+0000\n" +"POT-Creation-Date: 2019-01-17 08:59+0000\n" "PO-Revision-Date: 2017-03-15 14:20+0100\n" "Last-Translator: \n" "Language-Team: \n" @@ -791,16 +791,6 @@ msgid "Avances et prêts au personnel" msgstr "Voorschotten en leningen aan het personeel" #. module: l10n_be -#: model:account.chart.template,name:l10n_be.l10nbe_chart_template -msgid "Belgian PCMN" -msgstr "Belgische PCMN" - -#. module: l10n_be -#: model:ir.ui.menu,name:l10n_be.account_reports_be_statements_menu -msgid "Belgium Statements" -msgstr "" - -#. module: l10n_be #: model:account.account.tag,name:l10n_be.tax_tag_81_not_deductible msgid "Belgium VAT Form: Tax not deductible (grid 81)" msgstr "" @@ -1286,6 +1276,11 @@ msgid "Impôts étrangers" msgstr "Buitenlandse belastingen" #. module: l10n_be +#: model:account.account.template,name:l10n_be.l10nbe_chart_template_liquidity_transfer +msgid "Liquidity Transfer" +msgstr "" + +#. module: l10n_be #: model:account.account.template,name:l10n_be.a6100 msgid "Loyers divers" msgstr "Diverse huren" @@ -1303,7 +1298,8 @@ msgstr "Grondstoffen" #. module: l10n_be #: model:account.account.template,name:l10n_be.a24011 msgid "Matériel de bureau de bâtiments administratifs et commerciaux" -msgstr "Kantooruitrusting voor gebouwen met adminestratieve en commerciële functie" +msgstr "" +"Kantooruitrusting voor gebouwen met adminestratieve en commerciële functie" #. module: l10n_be #: model:account.account.template,name:l10n_be.a24010 @@ -1583,7 +1579,8 @@ msgstr "Bezoldigingen: Bestuurders of zaakvoerders" #. module: l10n_be #: model:account.account.template,name:l10n_be.a6204 msgid "Rémunérations: Autres membres du personnel" -msgstr "Bezoldigingen: Andere personeelsleden, verbonden met een arbeidsovereenkomst" +msgstr "" +"Bezoldigingen: Andere personeelsleden, verbonden met een arbeidsovereenkomst" #. module: l10n_be #: model:account.account.template,name:l10n_be.a6202 @@ -1666,6 +1663,7 @@ msgid "T.V.A. à payer - Intra-communautaire" msgstr "Te betalen BTW - Intracommunautair" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_0 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-00 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-00-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-00-S @@ -1695,6 +1693,11 @@ msgid "TVA 0% EU" msgstr "BTW 0% EU" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_0_eu +msgid "TVA 0% EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-00-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-00-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-00-ROW-CC @@ -1703,6 +1706,7 @@ msgid "TVA 0% Non EU" msgstr "BTW 0% Non EU" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_12 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-S @@ -1713,6 +1717,7 @@ msgid "TVA 12%" msgstr "BTW 12%" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_12_cocont #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-12-CC @@ -1763,6 +1768,11 @@ msgid "TVA 12% EU Récupérable - Biens divers" msgstr "BTW 12% EU Verhaalbaar - Diverse goederen" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_12_eu +msgid "TVA 12% EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-12-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-12-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-12-ROW-CC @@ -1784,6 +1794,12 @@ msgid "TVA 12% Non EU Récupérable" msgstr "BTW 12% Niet EU Verhaalbaar" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_12_non_eu +msgid "TVA 12% Non EU." +msgstr "" + +#. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_21 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-S @@ -1794,6 +1810,7 @@ msgid "TVA 21%" msgstr "BTW 21%" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_21_cocont #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-21-CC @@ -1839,6 +1856,11 @@ msgid "TVA 21% EU Récupérable" msgstr "BTW 21% EU Verhaalbaar" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_21_eu +msgid "TVA 21% EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-21-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-21-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-21-ROW-CC @@ -1860,11 +1882,21 @@ msgid "TVA 21% Non EU Récupérable" msgstr "BTW 21% Niet EU Verhaalbaar" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_21_non_eu +msgid "TVA 21% Non EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_TVA-21-inclus-dans-prix msgid "TVA 21% TTC" msgstr "VAT 21% (inclusief BTW)" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_21_50_deductible +msgid "TVA 21%, 50% Deductible." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-CAR-EXC-C2 msgid "TVA 50% Déductible - Frais de voiture (Prix Excl.)" msgstr "BTW 5°% Aftrekbaar - Auto kosten (Prijs Excl.)" @@ -1875,6 +1907,7 @@ msgid "TVA 50% Non Déductible - Frais de voiture (Prix Excl.)" msgstr "BTW 50% Niet Aftrekbaar - Auto kosten (Prijs Excl.)" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_6 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06 #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-G #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-S @@ -1885,6 +1918,7 @@ msgid "TVA 6%" msgstr "BTW 6%" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_6_cocont #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-06-CC @@ -1930,6 +1964,11 @@ msgid "TVA 6% EU Récupérable" msgstr "BTW 6% EU Verhaalbaar" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_6_eu +msgid "TVA 6% EU." +msgstr "" + +#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V81-06-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-06-ROW-CC #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V83-06-ROW-CC @@ -1951,6 +1990,11 @@ msgid "TVA 6% Non EU Récupérable" msgstr "BTW 6% Niet EU Verhaalbaar" #. module: l10n_be +#: model:account.tax.group,name:l10n_be.tax_group_tva_6_non_eu +msgid "TVA 6% Non EU." +msgstr "" + +#. module: l10n_be #: model:account.account.template,name:l10n_be.a4118 msgid "Taxe d'égalisation due" msgstr "Verschuldigde egalisatiebelasting" @@ -1966,11 +2010,6 @@ msgid "Terrains" msgstr "Terreinen" #. module: l10n_be -#: model:account.account.template,name:l10n_be.trans -msgid "Transferts" -msgstr "Transferten" - -#. module: l10n_be #: model:account.tax.template,description:l10n_be.attn_VAT-IN-V82-CAR-EXC-C1 msgid "VAT 50% Non Déductible - Frais de voiture" msgstr "BTW 50% Niet Aftrekbaar - Auto kosten" diff --git a/addons/l10n_be_invoice_bba/data/mail_template_data.xml b/addons/l10n_be_invoice_bba/data/mail_template_data.xml index e12f7745b91..5e6bfecdfb7 100644 --- a/addons/l10n_be_invoice_bba/data/mail_template_data.xml +++ b/addons/l10n_be_invoice_bba/data/mail_template_data.xml @@ -25,7 +25,7 @@ This invoice is already paid. % else: Please remit payment at your earliest convenience. - % if object.reference_type == 'bba' and object.reference: + % if object.invoice_reference_type=='structured' and object.reference: Please use the following communication for your payment: <strong>${object.reference}</strong>. % endif <br /><br /> diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index 99996615094..2b5bfe2a2db 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -78,6 +78,7 @@ class Channel(models.Model): ('chat', 'Chat Discussion'), ('channel', 'Channel')], 'Channel Type', default='channel') + is_chat = fields.Boolean(string='Is a chat', compute='_compute_is_chat', default=False) description = fields.Text('Description') uuid = fields.Char('UUID', size=50, index=True, default=lambda self: str(uuid4()), copy=False) email_send = fields.Boolean('Send messages by email', default=False) @@ -178,6 +179,12 @@ class Channel(models.Model): for record in self: record.is_member = record in membership_ids + @api.multi + def _compute_is_chat(self): + for record in self: + if record.channel_type == 'chat': + record.is_chat = True + @api.onchange('public') def _onchange_public(self): if self.public == 'public': @@ -384,7 +391,7 @@ class Channel(models.Model): if moderation_status == 'rejected': return self.env['mail.message'] - self.filtered(lambda channel: channel.channel_type == 'chat').mapped('channel_last_seen_partner_ids').write({'is_pinned': True}) + self.filtered(lambda channel: channel.is_chat).mapped('channel_last_seen_partner_ids').write({'is_pinned': True}) message = super(Channel, self.with_context(mail_create_nosubscribe=True)).message_post(message_type=message_type, moderation_status=moderation_status, **kwargs) diff --git a/addons/mail/static/src/js/utils.js b/addons/mail/static/src/js/utils.js index 4749ba11b20..740be8eb54d 100644 --- a/addons/mail/static/src/js/utils.js +++ b/addons/mail/static/src/js/utils.js @@ -22,7 +22,7 @@ function _parseAndTransform(nodes, transformFunction) { // Suggested URL Javascript regex of http://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url // Adapted to make http(s):// not required if (and only if) www. is given. So `should.notmatch` does not match. -var urlRegexp = /\b(?:https?:\/\/\d{1,3}(?:\.\d{1,3}){3}|(?:https?:\/\/|(?:www\.))[-a-z0-9@:%._+~#=]{2,256}\.[a-z]{2,13})\b(?:[-a-z0-9@:%_+.~#?&'$//=]*)/gi; +var urlRegexp = /\b(?:https?:\/\/\d{1,3}(?:\.\d{1,3}){3}|(?:https?:\/\/|(?:www\.))[-a-z0-9@:%._+~#=]{2,256}\.[a-z]{2,13})\b(?:[-a-z0-9@:%_+.~#?&'$//=;]*)/gi; function linkify(text, attrs) { attrs = attrs || {}; if (attrs.target === undefined) { diff --git a/addons/mass_mailing/static/src/scss/mass_mailing.scss b/addons/mass_mailing/static/src/scss/mass_mailing.scss index e13cea29676..873e1b9dea0 100644 --- a/addons/mass_mailing/static/src/scss/mass_mailing.scss +++ b/addons/mass_mailing/static/src/scss/mass_mailing.scss @@ -18,3 +18,9 @@ .o_white_body { background-color: white; } + +.o_mass_mailing_unsubscribed { + margin-left: 20px; + color: #005326; + font-size: 90%; +} diff --git a/addons/mass_mailing/views/unsubscribe_templates.xml b/addons/mass_mailing/views/unsubscribe_templates.xml index 621119c35b6..dfad695abf6 100644 --- a/addons/mass_mailing/views/unsubscribe_templates.xml +++ b/addons/mass_mailing/views/unsubscribe_templates.xml @@ -32,6 +32,10 @@ <input type="checkbox" class="mail_list_checkbox" name="contact_ids" t-att-value="list_id['id']" t-att-checked="None if list_id['id'] in opt_out_list_ids else 'checked'"/> <t t-esc="list_id.name"/> + <span t-if="list_id['id'] in opt_out_list_ids" + class="o_mass_mailing_unsubscribed"> + Unsubscribed + </span> </li> </t> </t> diff --git a/addons/mass_mailing_crm/data/mass_mailing_demo.xml b/addons/mass_mailing_crm/data/mass_mailing_demo.xml index 669f318341f..27d36357554 100644 --- a/addons/mass_mailing_crm/data/mass_mailing_demo.xml +++ b/addons/mass_mailing_crm/data/mass_mailing_demo.xml @@ -57,7 +57,7 @@ ${object.company_id.email} </a> % endif - ${object.company_id.website} + % if object.company_id.website | <a href="'%s' % object.company_id.website" style="text-decoration:none; color: #999999;"> ${object.company_id.website} diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 10ac3e19112..6310ffd8b82 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -540,7 +540,10 @@ class MrpProduction(models.Model): if move: old_qty = move[0].product_uom_qty if quantity > 0: - move[0].write({'product_uom_qty': quantity}) + move[0]._decrease_reserved_quanity(quantity) + move[0].with_context(do_not_unreserve=True).write({'product_uom_qty': quantity}) + move[0]._recompute_state() + move[0]._action_assign() move.unit_factor = quantity / move.raw_material_production_id.product_qty elif quantity < 0: # Do not remove 0 lines if move[0].quantity_done > 0: diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 096074b8157..ac20a46338e 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -188,6 +188,24 @@ class StockMove(models.Model): self.sudo().unlink() return processed_moves + def _decrease_reserved_quanity(self, quantity): + """ Decrease the reservation on move lines but keeps the + all other data. + """ + move_line_to_unlink = self.env['stock.move.line'] + for move in self: + reserved_quantity = quantity + for move_line in self.move_line_ids: + if move_line.product_uom_qty > reserved_quantity: + move_line.product_uom_qty = reserved_quantity + else: + move_line.product_uom_qty = 0 + reserved_quantity -= move_line.product_uom_qty + if not move_line.product_uom_qty and not move_line.qty_done: + move_line_to_unlink |= move_line + move_line_to_unlink.unlink() + return True + def _prepare_phantom_move_values(self, bom_line, product_qty, quantity_done): return { 'picking_id': self.picking_id.id if self.picking_id else False, diff --git a/addons/mrp/report/mrp_production_templates.xml b/addons/mrp/report/mrp_production_templates.xml index cbb042ff445..531b516f0da 100644 --- a/addons/mrp/report/mrp_production_templates.xml +++ b/addons/mrp/report/mrp_production_templates.xml @@ -7,9 +7,14 @@ <div class="page"> <div class="oe_structure"/> <div class="row"> - <div class="col-12"> + <div class="col-7"> <h2><span t-field="o.name"/></h2> </div> + <div class="col-5"> + <span class="text-right"> + <img t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('Code128', o.name, 600, 100)" style="width:350px;height:60px"/> + </span> + </div> </div> <div class="row mt32 mb32"> <div class="col-3" t-if="o.origin"> @@ -142,6 +147,56 @@ </t> </template> +<template id="label_production_view_pdf"> + <t t-call="web.basic_layout"> + <t t-foreach="docs" t-as="o"> + <div class="page"> + <table class="table table-condensed" style="border-bottom: 0px solid white !important;width: 3in;" t-if="o.move_finished_ids"> + <t t-set="lines" t-value="o.move_finished_ids"/> + <t t-foreach="lines" t-as="line"> + <t t-set="move_lines" t-value="line.move_line_ids.filtered(lambda x: x.state != 'done' and x.product_qty)"/> + <t t-if="o.state == 'done'"> + <t t-set="move_lines" t-value="line.move_line_ids.filtered(lambda x: x.state == 'done' and x.qty_done)"/> + </t> + <t t-foreach="move_lines" t-as="ml"> + <tr> + <th style="text-align: left;"> + <span t-field="line.product_id"/> + </th> + </tr> + <tr> + <td> + Quantity: + <span t-esc="ml.product_uom_qty" t-if="ml.state !='done'"/> + <span t-esc="ml.qty_done" t-if="ml.state =='done'"/> + <span t-field="ml.product_uom_id" groups="uom.group_uom"/> + </td> + </tr> + <tr> + <td style="text-align: center; vertical-align: middle;" class="col-5"> + <t t-if="ml.lot_id"> + <img t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('Code128', ml.lot_id.name, 600, 100)" style="width:100%;height:35px" alt="Barcode"/> + <span t-field="ml.lot_id.name"/> + </t> + </td> + </tr> + <tr> + <td style="text-align: center; vertical-align: middle;" class="col-5"> + <t t-if="line.product_id.barcode and not ml.lot_id"> + <img t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('Code128', line.product_id.barcode, 600, 100)" style="width:100%;height:20%;" alt="Barcode"/> + <span t-field="line.product_id.barcode"/> + </t> + </td> + </tr> + </t> + </t> + </table> + <div class="oe_structure"/> + </div> + </t> + </t> +</template> + <template id="production_message"> <t t-if="move.move_id.raw_material_production_id"> <t t-set="message">Consumed</t> diff --git a/addons/mrp/report/mrp_report_views_main.xml b/addons/mrp/report/mrp_report_views_main.xml index c78c6de4fa3..5805bea5820 100644 --- a/addons/mrp/report/mrp_report_views_main.xml +++ b/addons/mrp/report/mrp_report_views_main.xml @@ -19,5 +19,14 @@ report_type="qweb-pdf" print_report_name="'Bom Structure - %s' % object.display_name" /> + <report + string="Finished Product Label (PDF)" + id="action_report_finished_product" + model="mrp.production" + name="mrp.label_production_view_pdf" + file="mrp.label_production_view_pdf" + report_type="qweb-pdf" + print_report_name="'Finished products - %s' % object.name" + /> </data> </odoo> diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 8b2c4dd6c46..ac4ec19b748 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -629,6 +629,53 @@ class TestMrpOrder(TestMrpCommon): mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") + def test_product_produce_5(self): + """ Build 5 final products with different consumed lots, + then edit the finished quantity and update the Manufacturing + order quantity. Then check if the produced quantity do not + change and it is possible to close the MO. + """ + self.stock_location = self.env.ref('stock.stock_location_stock') + mo, bom, p_final, p1, p2 = self.generate_mo(tracking_base_1='lot') + self.assertEqual(len(mo), 1, 'MO should have been created') + + lot_1 = self.env['stock.production.lot'].create({ + 'name': 'lot1', + 'product_id': p1.id, + }) + lot_2 = self.env['stock.production.lot'].create({ + 'name': 'lot2', + 'product_id': p1.id, + }) + + self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 10, lot_id=lot_1) + self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 10, lot_id=lot_2) + + self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5) + mo.action_assign() + + produce_wizard = self.env['mrp.product.produce'].with_context({ + 'active_id': mo.id, + 'active_ids': [mo.id], + }).create({ + 'product_qty': 5.0, + }) + produce_wizard._onchange_product_qty() + + for produce_line in produce_wizard.produce_line_ids: + produce_line.qty_done = produce_line.qty_to_consume + produce_wizard.do_produce() + + mo.move_finished_ids.move_line_ids.qty_done -= 1 + update_quantity_wizard = self.env['change.production.qty'].create({ + 'mo_id': mo.id, + 'product_qty': 4, + }) + update_quantity_wizard.change_prod_qty() + + self.assertEqual(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity_done, 20, 'Update the produce quantity should not impact already produced quantity.') + mo.button_mark_done() + def test_product_produce_uom(self): plastic_laminate = self.env.ref('mrp.product_product_plastic_laminate') bom = self.env.ref('mrp.mrp_bom_plastic_laminate') diff --git a/addons/mrp/wizard/change_production_qty.py b/addons/mrp/wizard/change_production_qty.py index 6cda5d64f9b..b92616c9a50 100644 --- a/addons/mrp/wizard/change_production_qty.py +++ b/addons/mrp/wizard/change_production_qty.py @@ -53,7 +53,6 @@ class ChangeProductionQty(models.TransientModel): factor = production.product_uom_id._compute_quantity(production.product_qty - qty_produced, production.bom_id.product_uom_id) / production.bom_id.product_qty boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id) documents = {} - done_quantities = {move: move.quantity_done for move in production.move_raw_ids} for line, line_data in lines: move, old_qty, new_qty = production._update_raw_move(line, line_data) iterate_key = production._get_document_iterate_key(move) @@ -73,9 +72,6 @@ class ChangeProductionQty(models.TransientModel): production._log_downside_manufactured_quantity(finished_moves_modification) moves = production.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) moves._action_assign() - for move in production.move_raw_ids: - if move.quantity_done != done_quantities[move]: - move._set_quantity_done(done_quantities[move]) for wo in production.workorder_ids: operation = wo.operation_id if operation_bom_qty.get(operation.id): diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index afed11da38e..209c4556d7b 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -324,7 +324,7 @@ class PaymentAcquirer(models.Model): 'acquirers': acquirers, 'pms': self.env['payment.token'].search([ ('partner_id', '=', partner.id), - ('acquirer_id', 'in', acquirers.filtered(lambda acq: acq.payment_flow == 's2s').ids)]), + ('acquirer_id', 'in', acquirers.ids)]), } @api.multi diff --git a/addons/payment/wizards/payment_acquirer_onboarding_wizard.py b/addons/payment/wizards/payment_acquirer_onboarding_wizard.py index b53b25130c6..1b23489460c 100644 --- a/addons/payment/wizards/payment_acquirer_onboarding_wizard.py +++ b/addons/payment/wizards/payment_acquirer_onboarding_wizard.py @@ -114,12 +114,14 @@ class PaymentWizard(models.TransientModel): 'paypal_seller_account': self.paypal_seller_account, 'paypal_pdt_token': self.paypal_pdt_token, 'website_published': True, + 'environment': 'prod', }) if self.payment_method == 'stripe': new_env.ref('payment.payment_acquirer_stripe').write({ 'stripe_secret_key': self.stripe_secret_key, 'stripe_publishable_key': self.stripe_publishable_key, 'website_published': True, + 'environment': 'prod', }) if self.payment_method == 'manual': manual_acquirer = self._get_manual_payment_acquirer(new_env) @@ -131,6 +133,7 @@ class PaymentWizard(models.TransientModel): manual_acquirer.name = self.manual_name manual_acquirer.post_msg = self.manual_post_msg manual_acquirer.website_published = True + manual_acquirer.environment = 'prod' journal = manual_acquirer.journal_id if journal: diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index a4919e7c17f..b9543ca8453 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -572,14 +572,22 @@ class PosOrder(models.Model): states={'draft': [('readonly', False)]}, ) - @api.onchange('statement_ids', 'lines', 'lines') + @api.onchange('statement_ids', 'lines') def _onchange_amount_all(self): + amounts = {order_id: {'paid': 0, 'return': 0, 'untaxed': 0} for order_id in self.ids} + for order in self.env['account.bank.statement.line'].read_group([('pos_statement_id', 'in', self.ids)], ['pos_statement_id', 'amount'], ['pos_statement_id']): + amounts[order['pos_statement_id'][0]]['paid'] = order['amount'] + for order in self.env['account.bank.statement.line'].read_group(['&', ('pos_statement_id', 'in', self.ids), ('amount', '<', 0)], ['pos_statement_id', 'amount'], ['pos_statement_id']): + amounts[order['pos_statement_id'][0]]['return'] = order['amount'] + for order in self.env['pos.order.line'].read_group([('order_id', 'in', self.ids)], ['order_id', 'price_subtotal'], ['order_id']): + amounts[order['order_id'][0]]['untaxed'] = order['price_subtotal'] + for order in self: currency = order.pricelist_id.currency_id - order.amount_paid = sum(payment.amount for payment in order.statement_ids) - order.amount_return = sum(payment.amount < 0 and payment.amount or 0 for payment in order.statement_ids) + order.amount_paid = amounts[order.id]['paid'] + order.amount_return = amounts[order.id]['return'] order.amount_tax = currency.round(sum(self._amount_line_tax(line, order.fiscal_position_id) for line in order.lines)) - amount_untaxed = currency.round(sum(line.price_subtotal for line in order.lines)) + amount_untaxed = currency.round(amounts[order.id]['untaxed']) order.amount_total = order.amount_tax + amount_untaxed @api.onchange('partner_id') diff --git a/addons/point_of_sale/static/src/js/chrome.js b/addons/point_of_sale/static/src/js/chrome.js index d0d74c272f2..026682d5135 100644 --- a/addons/point_of_sale/static/src/js/chrome.js +++ b/addons/point_of_sale/static/src/js/chrome.js @@ -723,7 +723,7 @@ var Chrome = PosBaseWidget.extend(AbstractAction.prototype, { body = 'The Point of Sale could not be loaded due to a network problem.\n Please check your internet connection.'; }else if(err.message === 'TLSError'){ title = 'Https connection to IoT Box failed'; - body = 'Make sure you are using IoT Box v18.10 or higher.\n\n Navigate to ' + err.url + ' to accept the certificate of your IoT Box.'; + body = 'Make sure you are using IoT Box v18.12 or higher.\n\n Navigate to ' + err.url + ' to accept the certificate of your IoT Box.'; }else if(err.code === 200){ title = err.data.message; body = err.data.debug; diff --git a/addons/point_of_sale/tools/posbox/configuration/posbox_update.sh b/addons/point_of_sale/tools/posbox/configuration/posbox_update.sh index e994a60461f..8629d1a629a 100755 --- a/addons/point_of_sale/tools/posbox/configuration/posbox_update.sh +++ b/addons/point_of_sale/tools/posbox/configuration/posbox_update.sh @@ -1,6 +1,11 @@ #!/usr/bin/env bash sudo mount -o remount,rw / -sudo git --work-tree=/home/pi/odoo/ --git-dir=/home/pi/odoo/.git pull + +cd /home/pi/odoo +localbranch=$(git symbolic-ref -q --short HEAD) +localremote=$(git config branch.$localbranch.remote) +git fetch "${localremote}" "${localbranch}" --depth=1 +git reset "${localremote}"/"${localbranch}" --hard sudo mount -o remount,ro / (sleep 5 && sudo reboot) & diff --git a/addons/point_of_sale/tools/posbox/configuration/setup_ramdisks.sh b/addons/point_of_sale/tools/posbox/configuration/setup_ramdisks.sh index c9fc6c0da92..01470fff3c6 100755 --- a/addons/point_of_sale/tools/posbox/configuration/setup_ramdisks.sh +++ b/addons/point_of_sale/tools/posbox/configuration/setup_ramdisks.sh @@ -22,3 +22,7 @@ create_ramdisk "/tmp" "64M" # bind mount / so that we can get to the real /var and /etc mount --bind / /root_bypass_ramdisks + +# allow to cups server to save configuration file of printers +mount --bind /root_bypass_ramdisks/etc/cups /root_bypass_ramdisks/etc/cups +mount -o remount,rw /root_bypass_ramdisks/etc/cups /root_bypass_ramdisks/etc/cups diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cups-files.conf b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cups-files.conf new file mode 100644 index 00000000000..31864e16705 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cups-files.conf @@ -0,0 +1,93 @@ +# +# File/directory/user/group configuration file for the CUPS scheduler. +# See "man cups-files.conf" for a complete description of this file. +# + +# List of events that are considered fatal errors for the scheduler... +#FatalErrors config + +# Do we call fsync() after writing configuration or status files? +#SyncOnClose Yes + +# Default user and group for filters/backends/helper programs; this cannot be +# any user or group that resolves to ID 0 for security reasons... +#User lp +#Group lp + +# Administrator user group, used to match @SYSTEM in cupsd.conf policy rules... +# This cannot contain the Group value for security reasons... +SystemGroup lpadmin pi + + +# User that is substituted for unauthenticated (remote) root accesses... +#RemoteRoot remroot + +# Do we allow file: device URIs other than to /dev/null? +#FileDevice No + +# Permissions for configuration and log files... +#ConfigFilePerm 0640 +#LogFilePerm 00640 + +# Location of the file logging all access to the scheduler; may be the name +# "syslog". If not an absolute path, the value of ServerRoot is used as the +# root directory. Also see the "AccessLogLevel" directive in cupsd.conf. +AccessLog /var/log/cups/access_log + +# Location of cache files used by the scheduler... +#CacheDir /var/cache/cups + +# Location of data files used by the scheduler... +#DataDir /usr/share/cups + +# Location of the static web content served by the scheduler... +#DocumentRoot /usr/share/cups/doc-root + +# Location of the file logging all messages produced by the scheduler and any +# helper programs; may be the name "syslog". If not an absolute path, the value +# of ServerRoot is used as the root directory. Also see the "LogLevel" +# directive in cupsd.conf. +ErrorLog /var/log/cups/error_log + +# Location of fonts used by older print filters... +#FontPath /usr/share/cups/fonts + +# Location of LPD configuration +#LPDConfigFile + +# Location of the file logging all pages printed by the scheduler and any +# helper programs; may be the name "syslog". If not an absolute path, the value +# of ServerRoot is used as the root directory. Also see the "PageLogFormat" +# directive in cupsd.conf. +PageLog /var/log/cups/page_log + +# Location of the file listing all of the local printers... +#Printcap /run/cups/printcap + +# Format of the Printcap file... +#PrintcapFormat bsd +#PrintcapFormat plist +#PrintcapFormat solaris + +# Location of all spool files... +#RequestRoot /var/spool/cups + +# Location of helper programs... +#ServerBin /usr/lib/cups + +# SSL/TLS keychain for the scheduler... +#ServerKeychain ssl + +# Location of other configuration files... +ServerRoot /root_bypass_ramdisks/etc/cups + +# Location of Samba configuration file... +#SMBConfigFile + +# Location of scheduler state files... +#StateDir /run/cups + +# Location of scheduler/helper temporary files. This directory is emptied on +# scheduler startup and cannot be one of the standard (public) temporary +# directory locations for security reasons... +#TempDir /var/spool/cups/tmp diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cupsd.conf b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cupsd.conf new file mode 100644 index 00000000000..ee6cbbf93d4 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cupsd.conf @@ -0,0 +1,188 @@ +# +# Configuration file for the CUPS scheduler. See "man cupsd.conf" for a +# complete description of this file. +# + +# Log general information in error_log - change "warn" to "debug" +# for troubleshooting... +LogLevel warn +PageLogFormat + +# Deactivate CUPS' internal logrotating, as we provide a better one, especially +# LogLevel debug2 gets usable now +MaxLogSize 0 + +# Only listen for connections from the local machine. +Listen 0.0.0.0:631 +Listen /run/cups/cups.sock + +# Show shared printers on the local network. +Browsing On +BrowseLocalProtocols dnssd + +# Default authentication type, when authentication is required... +DefaultAuthType Basic + +# Web interface setting... +WebInterface Yes + +# Restrict access to the server... +<Location /> + Order allow,deny + Allow all +</Location> + +# Restrict access to the admin pages... +<Location /admin> + Order allow,deny + Allow all +</Location> + +# Restrict access to configuration files... +<Location /admin/conf> + AuthType Default + Require user @SYSTEM + Order allow +</Location> + +# Restrict access to log files... +<Location /admin/log> + AuthType Default + Require user @SYSTEM + Order allow +</Location> + +# Set the default printer/job policies... +<Policy default> + # Job/subscription privacy... + JobPrivateAccess default + JobPrivateValues default + SubscriptionPrivateAccess default + SubscriptionPrivateValues default + + # Job-related operations must be done by the owner or an administrator... + <Limit Create-Job Print-Job Print-URI Validate-Job> + Order deny,allow + </Limit> + + <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document> + Require user @OWNER @SYSTEM + Order deny,allow + </Limit> + + # All administration operations require an administrator to authenticate... + <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default CUPS-Get-Devices> + AuthType Default + Require user @SYSTEM + Order deny,allow + </Limit> + + # All printer operations require a printer operator to authenticate... + <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs> + AuthType Default + Require user @SYSTEM + Order deny,allow + </Limit> + + # Only the owner or an administrator can cancel or authenticate a job... + <Limit Cancel-Job CUPS-Authenticate-Job> + Require user @OWNER @SYSTEM + Order deny,allow + </Limit> + + <Limit All> + Order deny,allow + </Limit> +</Policy> + +# Set the authenticated printer/job policies... +<Policy authenticated> + # Job/subscription privacy... + JobPrivateAccess default + JobPrivateValues default + SubscriptionPrivateAccess default + SubscriptionPrivateValues default + + # Job-related operations must be done by the owner or an administrator... + <Limit Create-Job Print-Job Print-URI Validate-Job> + AuthType Default + Order deny,allow + </Limit> + + <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document> + AuthType Default + Require user @OWNER @SYSTEM + Order deny,allow + </Limit> + + # All administration operations require an administrator to authenticate... + <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default> + AuthType Default + Require user @SYSTEM + Order deny,allow + </Limit> + + # All printer operations require a printer operator to authenticate... + <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs> + AuthType Default + Require user @SYSTEM + Order deny,allow + </Limit> + + # Only the owner or an administrator can cancel or authenticate a job... + <Limit Cancel-Job CUPS-Authenticate-Job> + AuthType Default + Require user @OWNER @SYSTEM + Order deny,allow + </Limit> + + <Limit All> + Order deny,allow + </Limit> +</Policy> + +# Set the kerberized printer/job policies... +<Policy kerberos> + # Job/subscription privacy... + JobPrivateAccess default + JobPrivateValues default + SubscriptionPrivateAccess default + SubscriptionPrivateValues default + + # Job-related operations must be done by the owner or an administrator... + <Limit Create-Job Print-Job Print-URI Validate-Job> + AuthType Negotiate + Order deny,allow + </Limit> + + <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document> + AuthType Negotiate + Require user @OWNER @SYSTEM + Order deny,allow + </Limit> + + # All administration operations require an administrator to authenticate... + <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default> + AuthType Default + Require user @SYSTEM + Order deny,allow + </Limit> + + # All printer operations require a printer operator to authenticate... + <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs> + AuthType Default + Require user @SYSTEM + Order deny,allow + </Limit> + + # Only the owner or an administrator can cancel or authenticate a job... + <Limit Cancel-Job CUPS-Authenticate-Job> + AuthType Negotiate + Require user @OWNER @SYSTEM + Order deny,allow + </Limit> + + <Limit All> + Order deny,allow + </Limit> +</Policy>
\ No newline at end of file diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/nginx/sites-enabled/default b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/nginx/sites-enabled/default index b85a093785a..cd95a43753a 100644 --- a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/nginx/sites-enabled/default +++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/nginx/sites-enabled/default @@ -76,16 +76,16 @@ server { # You can move that to a different file under sites-available/ and symlink that # to sites-enabled/ to enable it. # -#server { -# listen 80; +server { + listen 80; # listen [::]:80; # # server_name example.com; # -# root /var/www/example.com; -# index index.html; + root /var/www/; + index index.html; # # location / { # try_files $uri $uri/ =404; # } -#} +} diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/var/www/iot.jpg b/addons/point_of_sale/tools/posbox/overwrite_after_init/var/www/iot.jpg Binary files differnew file mode 100644 index 00000000000..863710d5ade --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/var/www/iot.jpg diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh index 254c8def2b0..85c1933f096 100755 --- a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh @@ -21,7 +21,70 @@ apt-get update && apt-get -y upgrade # Do not be too fast to upgrade to more recent firmware and kernel than 4.38 # Firmware 4.44 seems to prevent the LED mechanism from working -PKGS_TO_INSTALL="fswebcam python3-urllib3 cups printer-driver-all cups-ipp-utils adduser postgresql python3 python3-dateutil python3-decorator python3-docutils python3-feedparser python3-pil python3-jinja2 python3-ldap3 python3-lxml python3-mako python3-mock python3-openid python3-psutil python3-psycopg2 python3-babel python3-pydot python3-pyparsing python3-pypdf2 python3-reportlab python3-requests python3-simplejson python3-tz python3-vatnumber python3-werkzeug python3-serial python3-pip python3-dev localepurge vim mc mg screen iw hostapd git rsync console-data lightdm xserver-xorg-video-fbdev xserver-xorg-input-evdev iceweasel xdotool unclutter x11-utils openbox python3-netifaces rpi-update python3-passlib python3-libsass python3-qrcode python3-html2text python3-unittest2 python3-simplejson nginx-full dnsmasq" +PKGS_TO_INSTALL=" + fswebcam \ + nginx-full \ + dnsmasq \ + cups \ + printer-driver-all \ + cups-ipp-utils \ + localepurge \ + vim \ + mc \ + mg \ + screen \ + iw \ + hostapd \ + git \ + rsync \ + console-data \ + lightdm \ + xserver-xorg-video-fbdev \ + xserver-xorg-input-evdev \ + iceweasel \ + xdotool \ + unclutter \ + x11-utils \ + openbox \ + rpi-update \ + adduser \ + postgresql \ + python3 \ + python3-urllib3 \ + python3-dateutil \ + python3-decorator \ + python3-docutils \ + python3-feedparser \ + python3-pil \ + python3-jinja2 \ + python3-ldap3 \ + python3-lxml \ + python3-mako \ + python3-mock \ + python3-openid \ + python3-psutil \ + python3-psycopg2 \ + python3-babel \ + python3-pydot \ + python3-pyparsing \ + python3-pypdf2 \ + python3-reportlab \ + python3-requests \ + python3-simplejson \ + python3-tz \ + python3-vatnumber \ + python3-werkzeug \ + python3-serial \ + python3-pip \ + python3-dev \ + python3-netifaces \ + python3-passlib \ + python3-libsass \ + python3-qrcode \ + python3-html2text \ + python3-unittest2 \ + python3-simplejson" + echo "Acquire::Retries "16";" > /etc/apt/apt.conf.d/99acquire-retries # KEEP OWN CONFIG FILES DURING PACKAGE CONFIGURATION # http://serverfault.com/questions/259226/automatically-keep-current-version-of-config-files-when-apt-get-install @@ -84,6 +147,7 @@ fi if [ $SYSTEMD -eq 1 ]; then systemctl set-default graphical.target ln -fs /etc/systemd/system/autologin@.service /etc/systemd/system/getty.target.wants/getty@tty1.service + rm /etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service else update-rc.d lightdm enable 2 fi diff --git a/addons/point_of_sale/tools/posbox/posbox_create_image.sh b/addons/point_of_sale/tools/posbox/posbox_create_image.sh index 946d2cefbde..0c517da73d7 100755 --- a/addons/point_of_sale/tools/posbox/posbox_create_image.sh +++ b/addons/point_of_sale/tools/posbox/posbox_create_image.sh @@ -29,7 +29,7 @@ __base="$(basename ${__file} .sh)" MOUNT_POINT="${__dir}/root_mount" OVERWRITE_FILES_BEFORE_INIT_DIR="${__dir}/overwrite_before_init" OVERWRITE_FILES_AFTER_INIT_DIR="${__dir}/overwrite_after_init" -VERSION=saas-11.5 +VERSION=12.0 REPO=https://github.com/odoo/odoo.git if [ ! -f kernel-qemu ] || ! file_exists *raspbian*.img ; then diff --git a/addons/product/models/product.py b/addons/product/models/product.py index 7b8a60a9787..ef770c8ac38 100644 --- a/addons/product/models/product.py +++ b/addons/product/models/product.py @@ -414,7 +414,7 @@ class ProductProduct(models.Model): # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields # Use `load=False` to not call `name_get` for the `product_tmpl_id` - self.sudo().read(['name', 'default_code', 'product_tmpl_id', 'attribute_value_ids'], load=False) + self.sudo().read(['name', 'default_code', 'product_tmpl_id', 'attribute_value_ids', 'attribute_line_ids'], load=False) product_template_ids = self.sudo().mapped('product_tmpl_id').ids @@ -431,7 +431,7 @@ class ProductProduct(models.Model): supplier_info_by_template.setdefault(r.product_tmpl_id, []).append(r) for product in self.sudo(): # display only the attributes with multiple possible values on the template - variable_attributes = product.attribute_value_ids.filtered(lambda v: len(v.attribute_id.value_ids) > 1).mapped('attribute_id') + variable_attributes = product.attribute_line_ids.filtered(lambda l: len(l.value_ids) > 1).mapped('attribute_id') variant = product.attribute_value_ids._variant_name(variable_attributes) name = variant and "%s (%s)" % (product.name, variant) or product.name diff --git a/addons/product/views/product_views.xml b/addons/product/views/product_views.xml index 3d2a4306f66..12159290405 100644 --- a/addons/product/views/product_views.xml +++ b/addons/product/views/product_views.xml @@ -310,7 +310,7 @@ <label for="weight"/> <div class="o_row"> <field name="weight"/> - <span>kg</span> + <span><field name="weight_uom_name"/></span> </div> </group> <group name="packaging" string="Packaging" groups="product.group_stock_packaging"> diff --git a/addons/purchase/models/res_partner.py b/addons/purchase/models/res_partner.py index 75b849af619..32007e09ab4 100644 --- a/addons/purchase/models/res_partner.py +++ b/addons/purchase/models/res_partner.py @@ -11,15 +11,38 @@ class res_partner(models.Model): @api.multi def _compute_purchase_order_count(self): - PurchaseOrder = self.env['purchase.order'] - for partner in self: - partner.purchase_order_count = PurchaseOrder.search_count([('partner_id', 'child_of', partner.id)]) + # retrieve all children partners and prefetch 'parent_id' on them + all_partners = self.search([('id', 'child_of', self.ids)]) + all_partners.read(['parent_id']) + + purchase_order_groups = self.env['purchase.order'].read_group( + domain=[('partner_id', 'in', all_partners.ids)], + fields=['partner_id'], groupby=['partner_id'] + ) + for group in purchase_order_groups: + partner = self.browse(group['partner_id'][0]) + while partner: + if partner in self: + partner.purchase_order_count += group['partner_id_count'] + partner = partner.parent_id @api.multi def _compute_supplier_invoice_count(self): - Invoice = self.env['account.invoice'] - for partner in self: - partner.supplier_invoice_count = Invoice.search_count([('partner_id', 'child_of', partner.id), ('type', 'in', ('in_invoice', 'in_refund'))]) + # retrieve all children partners and prefetch 'parent_id' on them + all_partners = self.search([('id', 'child_of', self.ids), ('type', 'in', ('in_invoice', 'in_refund')]) + all_partners.read(['parent_id']) + + supplier_invoice_groups = self.env['account.invoice'].read_group( + domain=[('partner_id', 'in', all_partners.ids), + ('type', '=', 'in_invoice')], + fields=['partner_id'], groupby=['partner_id'] + ) + for group in supplier_invoice_groups: + partner = self.browse(group['partner_id'][0]) + while partner: + if partner in self: + partner.supplier_invoice_count += group['partner_id_count'] + partner = partner.parent_id @api.model def _commercial_fields(self): diff --git a/addons/purchase/report/purchase_bill.py b/addons/purchase/report/purchase_bill.py index f9af76ca424..a16ff44b60c 100644 --- a/addons/purchase/report/purchase_bill.py +++ b/addons/purchase/report/purchase_bill.py @@ -8,7 +8,7 @@ class PurchaseBillUnion(models.Model): _name = 'purchase.bill.union' _auto = False _description = 'Purchases & Bills Union' - _order = "purchase_order_id desc, vendor_bill_id desc" + _order = "date desc, name desc" name = fields.Char(string='Reference', readonly=True) reference = fields.Char(string='Source', readonly=True) diff --git a/addons/purchase/views/purchase_views.xml b/addons/purchase/views/purchase_views.xml index 9c1f9d409c4..e4d9fcd309e 100644 --- a/addons/purchase/views/purchase_views.xml +++ b/addons/purchase/views/purchase_views.xml @@ -220,6 +220,7 @@ <field name="taxes_id" widget="many2many_tags" domain="[('type_tax_use', '=', 'purchase')]" options="{'no_create': True}"/> <field name="date_planned" widget="date"/> <field name="account_analytic_id" colspan="2" groups="analytic.group_analytic_accounting"/> + <field name="analytic_tag_ids" groups="analytic.group_analytic_accounting" widget="many2many_tags" options="{'color_field': 'color'}"/> <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> </group> </group> diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 5ab5355ac24..04391491a4d 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -863,21 +863,6 @@ class SaleOrder(models.Model): self.ensure_one() return '%s %s' % (self.type_name, self.name) - @api.multi - def get_access_action(self, access_uid=None): - """ Instead of the classic form view, redirect to the online quote if it exists. """ - self.ensure_one() - user = access_uid and self.env['res.users'].sudo().browse(access_uid) or self.env.user - - if not self.sale_order_template_id or (not user.share and not self.env.context.get('force_website')): - return super(SaleOrder, self).get_access_action(access_uid) - return { - 'type': 'ir.actions.act_url', - 'url': self.get_portal_url(), - 'target': 'self', - 'res_id': self.id, - } - def _get_share_url(self, redirect=False, signup_partner=False, pid=None): self.ensure_one() if self.state not in ['sale', 'done']: diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml index 582c23404bf..ade6a2e3f52 100644 --- a/addons/sale/views/sale_views.xml +++ b/addons/sale/views/sale_views.xml @@ -313,6 +313,7 @@ <field name="qty_to_invoice" invisible="1"/> <field name="qty_delivered_manual" invisible="1"/> <field name="qty_delivered_method" invisible="1"/> + <field name="price_total" invisible="1"/> <field name="price_subtotal" invisible="1"/> <label for="product_uom_qty" string="Ordered Quantity"/> <div> @@ -479,6 +480,7 @@ <field name="product_uom_qty"/> <field name="product_uom" groups="uom.group_uom"/> <field name="price_subtotal"/> + <field name="price_total" invisible="1"/> <field name="price_unit"/> <field name="display_type"/> <templates> diff --git a/addons/sale_management/models/sale_order.py b/addons/sale_management/models/sale_order.py index bc32d716dab..afa13aed50d 100644 --- a/addons/sale_management/models/sale_order.py +++ b/addons/sale_management/models/sale_order.py @@ -114,6 +114,21 @@ class SaleOrder(models.Model): self.sale_order_template_id.mail_template_id.send_mail(order.id) return res + @api.multi + def get_access_action(self, access_uid=None): + """ Instead of the classic form view, redirect to the online quote if it exists. """ + self.ensure_one() + user = access_uid and self.env['res.users'].sudo().browse(access_uid) or self.env.user + + if not self.sale_order_template_id or (not user.share and not self.env.context.get('force_website')): + return super(SaleOrder, self).get_access_action(access_uid) + return { + 'type': 'ir.actions.act_url', + 'url': self.get_portal_url(), + 'target': 'self', + 'res_id': self.id, + } + class SaleOrderLine(models.Model): _inherit = "sale.order.line" diff --git a/addons/snailmail/static/src/scss/snailmail_external_layout_asset.scss b/addons/snailmail/static/src/scss/snailmail_external_layout_asset.scss index 340c17dda25..ae60f3d4458 100644 --- a/addons/snailmail/static/src/scss/snailmail_external_layout_asset.scss +++ b/addons/snailmail/static/src/scss/snailmail_external_layout_asset.scss @@ -14,6 +14,9 @@ padding-left: 10mm; } } +.o_background_footer { + padding-left: 8mm; +} .article>.page { > h1, > h2, > h3 { width: 50%; diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py index 3ecccb65beb..8e4bde1a2e2 100644 --- a/addons/stock_account/models/product.py +++ b/addons/stock_account/models/product.py @@ -50,6 +50,7 @@ class ProductTemplate(models.Model): class ProductProduct(models.Model): _inherit = 'product.product' + stock_value_currency_id = fields.Many2one('res.currency', compute='_compute_stock_value_currency') stock_value = fields.Float( 'Value', compute='_compute_stock_value') qty_at_date = fields.Float( @@ -126,6 +127,12 @@ class ProductProduct(models.Model): return sum(moves.mapped('remaining_value')), moves @api.multi + def _compute_stock_value_currency(self): + currency_id = self.env.user.company_id.currency_id + for product in self: + product.stock_value_currency_id = currency_id + + @api.multi @api.depends('stock_move_ids.product_qty', 'stock_move_ids.state', 'stock_move_ids.remaining_value', 'product_tmpl_id.cost_method', 'product_tmpl_id.standard_price', 'product_tmpl_id.categ_id.property_valuation') def _compute_stock_value(self): StockMove = self.env['stock.move'] @@ -191,12 +198,14 @@ class ProductProduct(models.Model): """ self.ensure_one() to_date = self.env.context.get('to_date') + ctx = self.env.context.copy() + ctx.pop('group_by', None) action = { 'name': _('Valuation at date'), 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'tree,form', - 'context': self.env.context, + 'context': ctx, } if self.valuation == 'real_time': action['res_model'] = 'account.move.line' diff --git a/addons/stock_account/views/stock_account_views.xml b/addons/stock_account/views/stock_account_views.xml index fd6ed2c27a5..a9dbf024f2e 100644 --- a/addons/stock_account/views/stock_account_views.xml +++ b/addons/stock_account/views/stock_account_views.xml @@ -77,7 +77,8 @@ <field name="qty_at_date"/> <field name="uom_id" groups="uom.group_uom"/> <field name="currency_id" invisible="1"/> - <field name="stock_value" sum="Stock Valuation" widget="monetary"/> + <field name="stock_value_currency_id" invisible="1"/> + <field name="stock_value" sum="Stock Valuation" widget="monetary" options="{'currency_field': 'stock_value_currency_id'}"/> <field name="cost_method" invisible="1"/> <button name="action_valuation_at_date_details" type="object" icon="fa-info-circle" aria-label="Valuation" title="Valuation" attrs="{'invisible': [('cost_method', '!=', 'fifo')]}" /> </tree> diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py index 80ee03d0dcf..2d7d1b49df5 100644 --- a/addons/survey/controllers/main.py +++ b/addons/survey/controllers/main.py @@ -254,6 +254,10 @@ class Survey(http.Controller): def print_survey(self, survey, token=None, **post): '''Display an survey in printable view; if <token> is set, it will grab the answers of the user_input_id that has <token>.''' + + if survey.auth_required and request.env.user == request.website.user_id: + return request.render("survey.auth_required", {'survey': survey, 'token': token}) + return request.render('survey.survey_print', {'survey': survey, 'token': token, diff --git a/addons/web/static/src/js/chrome/abstract_web_client.js b/addons/web/static/src/js/chrome/abstract_web_client.js index ea8fdc24a18..7082f26e69d 100644 --- a/addons/web/static/src/js/chrome/abstract_web_client.js +++ b/addons/web/static/src/js/chrome/abstract_web_client.js @@ -205,6 +205,10 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi }); } } else { + // ignore Chrome video internal error: https://crbug.com/809574 + if (!error && message === 'ResizeObserver loop limit exceeded') { + return; + } var traceback = error ? error.stack : ''; crash_manager.show_error({ type: _t("Odoo Client Error"), diff --git a/addons/web/static/src/js/chrome/apps_menu.js b/addons/web/static/src/js/chrome/apps_menu.js index f6535ed764c..eb7faaa971b 100644 --- a/addons/web/static/src/js/chrome/apps_menu.js +++ b/addons/web/static/src/js/chrome/apps_menu.js @@ -41,6 +41,9 @@ var AppsMenu = Widget.extend({ * Open the first app in the list of apps */ openFirstApp: function () { + if (!this._apps.length) { + return + } var firstApp = this._apps[0]; this._openApp(firstApp); }, diff --git a/addons/web/static/src/js/fields/special_fields.js b/addons/web/static/src/js/fields/special_fields.js index 5057b9de33f..e9dcbc592a3 100644 --- a/addons/web/static/src/js/fields/special_fields.js +++ b/addons/web/static/src/js/fields/special_fields.js @@ -122,7 +122,8 @@ var FieldTimezoneMismatch = FieldSelection.extend({ }); var FieldReportLayout = relational_fields.FieldMany2One.extend({ - supportedFieldTypes: ['many2one', 'selection'], + // this widget is not generic, so we disable its studio use + // supportedFieldTypes: ['many2one', 'selection'], events: _.extend({}, relational_fields.FieldMany2One.prototype.events, { 'click img': '_onImgClicked', }), diff --git a/addons/web/static/src/js/services/crash_manager.js b/addons/web/static/src/js/services/crash_manager.js index e1da816c8e5..31aa178dc1b 100644 --- a/addons/web/static/src/js/services/crash_manager.js +++ b/addons/web/static/src/js/services/crash_manager.js @@ -22,6 +22,7 @@ var map_title ={ var CrashManager = core.Class.extend({ init: function() { this.active = true; + this.isConnected = true; }, enable: function () { this.active = true; @@ -29,8 +30,29 @@ var CrashManager = core.Class.extend({ disable: function () { this.active = false; }, - rpc_error: function(error) { + handleLostConnection: function () { var self = this; + if (!this.isConnected) { + // already handled, nothing to do. This can happen when several + // rpcs are done in parallel and fail because of a lost connection. + return; + } + this.isConnected = false; + var delay = 2000; + core.bus.trigger('connection_lost'); + + setTimeout(function checkConnection() { + ajax.jsonRpc('/web/webclient/version_info', 'call', {}, {shadow:true}).then(function () { + core.bus.trigger('connection_restored'); + self.isConnected = true; + }).fail(function () { + // exponential backoff, with some jitter + delay = (delay * 1.5) + 500*Math.random(); + setTimeout(checkConnection, delay); + }); + }, delay); + }, + rpc_error: function(error) { if (!this.active) { return; } @@ -38,16 +60,7 @@ var CrashManager = core.Class.extend({ return; } if (error.code === -32098) { - core.bus.trigger('connection_lost'); - this.connection_lost = true; - var timeinterval = setInterval(function() { - var options = {shadow: true}; - ajax.jsonRpc('/web/webclient/version_info', 'call', {}, options).then(function () { - clearInterval(timeinterval); - core.bus.trigger('connection_restored'); - self.connection_lost = false; - }); - }, 2000); + this.handleLostConnection(); return; } var handler = core.crash_registry.get(error.data.name, true); diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js index 7f578da077d..f26a9cbe737 100644 --- a/addons/web/static/src/js/views/basic/basic_controller.js +++ b/addons/web/static/src/js/views/basic/basic_controller.js @@ -38,6 +38,10 @@ var BasicController = AbstractController.extend(FieldManagerMixin, { FieldManagerMixin.init.call(this, this.model); this.mode = params.mode || 'readonly'; this.handle = this.initialState.id; + // savingDef is used to ensure that we always wait for pending save + // operations to complete before checking if there are changes to + // discard when discardChanges is called + this.savingDef = $.when(); }, /** * @override @@ -102,14 +106,15 @@ var BasicController = AbstractController.extend(FieldManagerMixin, { return true; }, /** - * Waits for the mutex to be unlocked and then calls _.discardChanges. + * Waits for the mutex to be unlocked and for changes to be saved, then + * calls _.discardChanges. * This ensures that the confirm dialog isn't displayed directly if there is * a pending 'write' rpc. * * @see _.discardChanges */ discardChanges: function (recordID, options) { - return this.mutex.exec(function () {}) + return $.when(this.mutex.getUnlockedDef(), this.savingDef) .then(this._discardChanges.bind(this, recordID || this.handle, options)); }, /** @@ -183,11 +188,12 @@ var BasicController = AbstractController.extend(FieldManagerMixin, { // mutex-protected as commitChanges function of x2m has to be aware of // all final changes made to a row. var self = this; - return this.mutex.getUnlockedDef().then(function () { + this.savingDef = this.mutex.getUnlockedDef().then(function () { return self.renderer.commitChanges(recordID || self.handle).then(function () { return self.mutex.exec(self._saveRecord.bind(self, recordID, options)); }); }); + return this.savingDef; }, /** * @override diff --git a/addons/web/static/src/js/views/field_manager_mixin.js b/addons/web/static/src/js/views/field_manager_mixin.js index 343730cc7a4..bc61e92a89a 100644 --- a/addons/web/static/src/js/views/field_manager_mixin.js +++ b/addons/web/static/src/js/views/field_manager_mixin.js @@ -130,8 +130,10 @@ var FieldManagerMixin = { if ('offset' in data) { params.offset = data.offset; } - this.model.reload(data.id, params).then(function (db_id) { - data.on_success(self.model.get(db_id)); + this.mutex.exec(function () { + return self.model.reload(data.id, params).then(function (db_id) { + data.on_success(self.model.get(db_id)); + }); }); }, /** diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js index 2383a0ac730..8c43857277c 100644 --- a/addons/web/static/src/js/views/kanban/kanban_controller.js +++ b/addons/web/static/src/js/views/kanban/kanban_controller.js @@ -210,15 +210,17 @@ var KanbanController = BasicController.extend({ */ _onAddColumn: function (ev) { var self = this; - this.model.createGroup(ev.data.value, this.handle).then(function () { - var state = self.model.get(self.handle, {raw: true}); - var ids = _.pluck(state.data, 'res_id').filter(_.isNumber); - return self._resequenceColumns(ids); - }).then(function () { - return self.update({}, {reload: false}); - }).then(function () { - self._updateButtons(); - self.renderer.quickCreateToggleFold(); + this.mutex.exec(function () { + return self.model.createGroup(ev.data.value, this.handle).then(function () { + var state = self.model.get(self.handle, {raw: true}); + var ids = _.pluck(state.data, 'res_id').filter(_.isNumber); + return self._resequenceColumns(ids); + }).then(function () { + return self.update({}, {reload: false}); + }).then(function () { + self._updateButtons(); + self.renderer.quickCreateToggleFold(); + }); }); }, /** @@ -271,11 +273,16 @@ var KanbanController = BasicController.extend({ * @private */ _onButtonNew: function () { + var self = this; var state = this.model.get(this.handle, {raw: true}); var quickCreateEnabled = this.quickCreateEnabled && viewUtils.isQuickCreateEnabled(state); if (this.on_create === 'quick_create' && quickCreateEnabled && state.data.length) { - // Activate the quick create in the first column - this.renderer.addQuickCreate(); + // activate the quick create in the first column when the mutex is + // unlocked, to ensure that there is no pending re-rendering that + // would remove it (e.g. if we are currently adding a new column) + this.mutex.getUnlockedDef().then(function () { + self.renderer.addQuickCreate(); + }); } else if (this.on_create && this.on_create !== 'quick_create') { // Execute the given action this.do_action(this.on_create, { diff --git a/addons/web/static/src/js/views/pivot/pivot_model.js b/addons/web/static/src/js/views/pivot/pivot_model.js index 5a6423fbe34..607cf2d7b2f 100644 --- a/addons/web/static/src/js/views/pivot/pivot_model.js +++ b/addons/web/static/src/js/views/pivot/pivot_model.js @@ -321,7 +321,7 @@ var PivotModel = AbstractModel.extend({ }; } return { - colGroupBys: this.data.main_col.groupbys, + colGroupBys: this.data.main_col.root.groupbys, context: this.data.context, domain: this.data.domain, compare: this.data.compare, @@ -331,7 +331,7 @@ var PivotModel = AbstractModel.extend({ mainColWidth: this.data.main_col.width, measures: this.data.measures, rows: !isRaw && this._computeRows(), - rowGroupBys: this.data.main_row.groupbys, + rowGroupBys: this.data.main_row.root.groupbys, sortedColumn: this.data.sorted_column, }; }, @@ -432,22 +432,19 @@ var PivotModel = AbstractModel.extend({ return this._loadData(); } - var old_row_root = this.data.main_row.root; - var old_col_root = this.data.main_col.root; + var old_row = this.data.main_row; + var old_col = this.data.main_col; return this._loadData().then(function () { - var new_groupby_length; if (!('groupBy' in params) && !('pivot_row_groupby' in (params.context || {}))) { // we only update the row groupbys according to the old groupbys // if we don't have the key 'groupBy' in params. In that case, // we want to have the full open state for the groupbys. - self._updateTree(old_row_root, self.data.main_row.root); - new_groupby_length = self._getHeaderDepth(self.data.main_row.root) - 1; - self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length); + self._updateTree(old_row.root, self.data.main_row.root); + self._updateMainGroupBys(old_row, self.data.main_row); } - self._updateTree(old_col_root, self.data.main_col.root); - new_groupby_length = self._getHeaderDepth(self.data.main_col.root) - 1; - self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length); + self._updateTree(old_col.root, self.data.main_col.root); + self._updateMainGroupBys(old_col, self.data.main_col); }); }, /** @@ -1052,9 +1049,6 @@ var PivotModel = AbstractModel.extend({ } } - this.data.main_row.groupbys = rowGroupBys; - this.data.main_col.groupbys = colGroupBys; - main_row_header.other_root = main_col_header; main_col_header.other_root = main_row_header; @@ -1124,6 +1118,19 @@ var PivotModel = AbstractModel.extend({ } }, /** + * Updates the groupBys that the main[row | col] holds + * + * @private + * @param {Object} old: The main abstract header before reload + * @param {Object} main: The main abstract header after reload + * + */ + _updateMainGroupBys: function (old, main) { + var new_groupby_length = this._getHeaderDepth(main.root) - 1; + var new_groupby_list = old.root.groupbys.slice(0, new_groupby_length); + main.root.groupbys = new_groupby_list; + }, + /** * @param {Object} old_tree * @param {Object} new_tree */ diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 15999ca952e..21dcef78978 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -150,6 +150,61 @@ QUnit.module('relational_fields', { } }, function () { + QUnit.test('search more pager is reset when doing a new search', function (assert) { + assert.expect(6); + for(var i = 10; i < 180; i++) { + this.data.partner.records.push({ + id: i, + display_name: "Partner " + i, + }); + } + + function stringToEvent ($element, string) { + for (var i = 0; i < string.length; i++) { + var keyAscii = string.charCodeAt(i); + $element.val($element.val()+string[i]); + $element.trigger($.Event('keyup', {which: keyAscii, keyCode:keyAscii})); + } + $element.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER, keyCode:$.ui.keyCode.ENTER})); + } + + this.data.partner.fields.datetime.searchable = true; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form string="Partners">' + + '<sheet>' + + '<group>' + + '<field name="trululu"/>' + + '</group>' + + '</sheet>' + + '</form>', + archs: { + 'partner,false,list': '<tree><field name="display_name"/></tree>', + 'partner,false,search': '<search><field name="datetime"/><field name="display_name"/></search>', + }, + res_id: 1, + }); + + form.$buttons.find('.o_form_button_edit').click(); + var $dropdown = form.$('.o_field_many2one input').autocomplete('widget'); + form.$('.o_field_many2one input').click(); + $dropdown.find('.o_m2o_dropdown_option:contains(Search)').mouseenter().click(); + $('.modal .o_pager_next').click(); + + assert.strictEqual($('.o_pager_limit').text(), "1173", "there should be 173 records"); + assert.strictEqual($('.o_pager_value').text(), "181-160", "should display the second page"); + assert.strictEqual($('tr.o_data_row').length, 80, "should display 80 record"); + + stringToEvent($('.modal .o_searchview_input'), 'first'); + + assert.strictEqual($('.o_pager_limit').text(), "11", "there should be 1 record"); + assert.strictEqual($('.o_pager_value').text(), "11-1", "should display the first page"); + assert.strictEqual($('tr.o_data_row').length, 1, "should display 1 record"); + form.destroy(); + }); + QUnit.test('x2many default_order multiple fields', function (assert) { assert.expect(7); @@ -446,6 +501,48 @@ QUnit.module('relational_fields', { form.destroy(); }); + + form.destroy(); + }); + + QUnit.test('quickly switch between pages in one2many list', function (assert) { + assert.expect(2); + + this.data.partner.records[0].turtles = [1, 2, 3]; + + var readDefs = [$.when(), $.Deferred(), $.Deferred()]; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form string="Partners">' + + '<field name="turtles">' + + '<tree limit="1">' + + '<field name="display_name"/>' + + '</tree>' + + '</field>' + + '</form>', + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'read') { + var recordID = args.args[0][0]; + return $.when(readDefs[recordID - 1]).then(_.constant(result)); + } + return result; + }, + res_id: 1, + }); + + form.$('.o_field_widget[name=turtles] .o_pager_next').click(); + form.$('.o_field_widget[name=turtles] .o_pager_next').click(); + + readDefs[1].resolve(); + + assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_data_cell').text(), 'donatello'); + + readDefs[2].resolve(); + + assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_data_cell').text(), 'raphael'); QUnit.test('many2many read, field context is properly sent', function (assert) { assert.expect(4); diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index e1cb3277dba..f70c21de765 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -7189,6 +7189,69 @@ QUnit.module('Views', { form.destroy(); }); + QUnit.test('call canBeRemoved while saving', function (assert) { + assert.expect(10); + + this.data.partner.onchanges = { + foo: function (obj) { + obj.display_name = obj.foo === 'trigger onchange' ? 'changed' : 'default'; + }, + }; + + var onchangeDef; + var createDef = $.Deferred(); + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form><field name="display_name"/><field name="foo"/></form>', + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'onchange') { + return $.when(onchangeDef).then(_.constant(result)); + } + if (args.method === 'create') { + return $.when(createDef).then(_.constant(result)); + } + return result; + }, + }); + + // edit foo to trigger a delayed onchange + onchangeDef = $.Deferred(); + testUtils.fields.editInput(form.$('.o_field_widget[name=foo]'), 'trigger onchange'); + + assert.strictEqual(form.$('.o_field_widget[name=display_name]').val(), 'default'); + + // save (will wait for the onchange to return), and will be delayed as well + testUtils.dom.click(form.$buttons.find('.o_form_button_save')); + + assert.hasClass(form.$('.o_form_view'), 'o_form_editable'); + assert.strictEqual(form.$('.o_field_widget[name=display_name]').val(), 'default'); + + // simulate a click on the breadcrumbs to leave the form view + form.canBeRemoved(); + + assert.hasClass(form.$('.o_form_view'), 'o_form_editable'); + assert.strictEqual(form.$('.o_field_widget[name=display_name]').val(), 'default'); + + // unlock the onchange + onchangeDef.resolve(); + + assert.hasClass(form.$('.o_form_view'), 'o_form_editable'); + assert.strictEqual(form.$('.o_field_widget[name=display_name]').val(), 'changed'); + + // unlock the create + createDef.resolve(); + + assert.hasClass(form.$('.o_form_view'), 'o_form_readonly'); + assert.strictEqual(form.$('.o_field_widget[name=display_name]').text(), 'changed'); + assert.containsNone(document.body, '.modal', + "should not display the 'Changes will be discarded' dialog"); + + form.destroy(); + }); + QUnit.module('FormViewTABMainButtons'); QUnit.test('using tab in an empty required string field should not move to the next field',function(assert) { diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index 86538ab5eb0..47af910083d 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -2110,6 +2110,67 @@ QUnit.module('Views', { kanban.destroy(); }); + QUnit.test('quick create record while adding a new column', function (assert) { + assert.expect(10); + + var def = $.Deferred(); + var kanban = createView({ + View: KanbanView, + model: 'partner', + data: this.data, + arch: '<kanban on_create="quick_create">' + + '<templates><t t-name="kanban-box">' + + '<div><field name="foo"/></div>' + + '</t></templates>' + + '</kanban>', + groupBy: ['product_id'], + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'name_create' && args.model === 'product') { + return def.then(_.constant(result)); + } + return result; + }, + }); + + assert.containsN(kanban, '.o_kanban_group', 2); + assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 2); + + // add a new column + assert.containsOnce(kanban, '.o_column_quick_create'); + assert.isNotVisible(kanban.$('.o_column_quick_create input')); + + testUtils.dom.click(kanban.$('.o_quick_create_folded')); + + assert.isVisible(kanban.$('.o_column_quick_create input')); + + testUtils.fields.editInput(kanban.$('.o_column_quick_create input'), 'new column'); + testUtils.dom.click(kanban.$('.o_column_quick_create button.o_kanban_add')); + + assert.containsN(kanban, '.o_kanban_group', 2); + + // click to add a new record + testUtils.dom.click(kanban.$buttons.find('.o-kanban-button-new')); + + // should wait for the column to be created (and view to be re-rendered + // before opening the quick create + assert.containsNone(kanban, '.o_kanban_quick_create'); + + // unlock column creation + def.resolve(); + + assert.containsN(kanban, '.o_kanban_group', 3); + assert.containsOnce(kanban, '.o_kanban_quick_create'); + + // quick create record in first column + testUtils.fields.editInput(kanban.$('.o_kanban_quick_create input'), 'new record'); + testUtils.dom.click(kanban.$('.o_kanban_quick_create .o_kanban_add')); + + assert.containsN(kanban, '.o_kanban_group:first .o_kanban_record', 3); + + kanban.destroy(); + }); + QUnit.test('many2many_tags in kanban views', function (assert) { assert.expect(12); diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js index 930fadbbb7b..6263d34bfb3 100644 --- a/addons/web/static/tests/views/pivot_tests.js +++ b/addons/web/static/tests/views/pivot_tests.js @@ -923,6 +923,80 @@ QUnit.module('Views', { pivot.destroy(); }); + QUnit.test('correctly remove pivot_ keys from the context', function (assert) { + assert.expect(5); + + this.data.partner.fields.amount = {string: "Amount", type: "float"}; + + // Equivalent to loading with default filter + var pivot = createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '<pivot>' + + '<field name="date" interval="day" type="col"/>' + + '<field name="amount" type="measure"/>' + + '</pivot>', + viewOptions: { + context: { + pivot_measures: ['foo'], + pivot_column_groupby: ['customer'], + pivot_row_groupby: ['product_id'], + }, + }, + }); + + // Equivalent to unload the filter + var reloadParams = { + context: {}, + }; + pivot.reload(reloadParams); + + assert.deepEqual(pivot.getContext(), { + pivot_column_groupby: ['customer'], + pivot_measures: ['foo'], + pivot_row_groupby: ['product_id'], + }, "context should be correct"); + + // Let's get rid of the rows groupBy + pivot.$('tbody .o_pivot_header_cell_opened').click(); + + assert.deepEqual(pivot.getContext(), { + pivot_column_groupby: ['customer'], + pivot_measures: ['foo'], + pivot_row_groupby: [], + }, "context should be correct"); + + // And now, get rid of the col groupby + pivot.$('thead .o_pivot_header_cell_opened').click(); + + assert.deepEqual(pivot.getContext(), { + pivot_column_groupby: [], + pivot_measures: ['foo'], + pivot_row_groupby: [], + }, "context should be correct"); + + pivot.$('tbody .o_pivot_header_cell_closed').click(); + pivot.$('.o_pivot_field_menu li[data-field=product_id] a').click(); + + assert.deepEqual(pivot.getContext(), { + pivot_column_groupby: [], + pivot_measures: ['foo'], + pivot_row_groupby: ['product_id'], + }, "context should be correct"); + + pivot.$('thead .o_pivot_header_cell_closed').click(); + pivot.$('.o_pivot_field_menu li[data-field=customer] a').click(); + + assert.deepEqual(pivot.getContext(), { + pivot_column_groupby: ['customer'], + pivot_measures: ['foo'], + pivot_row_groupby: ['product_id'], + }, "context should be correct"); + + pivot.destroy(); + }); + QUnit.test('correctly uses pivot_ keys from the context', function (assert) { assert.expect(7); diff --git a/addons/web_editor/controllers/main.py b/addons/web_editor/controllers/main.py index 1b3f03966f3..40cf2aba5ef 100644 --- a/addons/web_editor/controllers/main.py +++ b/addons/web_editor/controllers/main.py @@ -416,7 +416,17 @@ class Web_Editor(http.Controller): # Create a view to extend the template which adds the original file to link the new modified version instead IrUiView = request.env["ir.ui.view"] - view_to_xpath = IrUiView.get_related_views(bundle_xmlid, bundles=True).filtered(lambda v: v.arch.find(url) >= 0) + + def views_linking_url(view): + """ + Returns whether the view arch has some html link tag linked to the url. + + (note: searching for the URL string is not enough as it could appear in a comment or an xpath expression.) + """ + return bool(etree.XML(view.arch).xpath("link[@href='{}']".format(url))) + + view_to_xpath = IrUiView.get_related_views(bundle_xmlid, bundles=True).filtered(views_linking_url) + new_view = { 'name': custom_url, 'key': 'web_editor.scss_%s' % str(uuid.uuid4())[:6], diff --git a/addons/web_tour/__manifest__.py b/addons/web_tour/__manifest__.py index 80f44db9165..5ff9710e855 100644 --- a/addons/web_tour/__manifest__.py +++ b/addons/web_tour/__manifest__.py @@ -21,7 +21,7 @@ Odoo Web tours. 'data/web_tour_demo.xml', ], 'qweb': [ - "static/src/xml/*.xml", + "static/src/xml/debug_manager.xml", ], 'auto_install': True } diff --git a/addons/web_tour/static/src/js/tip.js b/addons/web_tour/static/src/js/tip.js index 971af4e7c7f..8b3a8d04566 100644 --- a/addons/web_tour/static/src/js/tip.js +++ b/addons/web_tour/static/src/js/tip.js @@ -8,6 +8,7 @@ var _t = core._t; var Tip = Widget.extend({ template: "Tip", + xmlDependencies: ['/web_tour/static/src/xml/tip.xml'], events: { click: '_onTipClicked', mouseenter: "_to_info_mode", diff --git a/addons/web_tour/static/src/js/tour_manager.js b/addons/web_tour/static/src/js/tour_manager.js index 7b787e3f059..f0521441a9e 100644 --- a/addons/web_tour/static/src/js/tour_manager.js +++ b/addons/web_tour/static/src/js/tour_manager.js @@ -32,7 +32,7 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { this.tours = {}; this.consumed_tours = consumed_tours || []; this.running_tour = local_storage.getItem(get_running_key()); - this.running_step_delay = parseInt(local_storage.getItem(get_running_delay_key()), 10) || 10; + this.running_step_delay = parseInt(local_storage.getItem(get_running_delay_key()), 10) || 0; this.edition = (_.last(session.server_version_info) === 'e') ? 'enterprise' : 'community'; this._log = []; console.log('Tour Manager is ready. running_tour=' + this.running_tour); @@ -171,7 +171,10 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { if (this.running_tour && this.running_tour_timeout === undefined) { this._set_running_tour_timeout(this.running_tour, this.active_tooltips[this.running_tour]); } - this._check_for_tooltip(this.active_tooltips[tour_name], tour_name); + var self = this; + setTimeout(function () { + self._check_for_tooltip(self.active_tooltips[tour_name], tour_name); + }); } else { for (var tourName in this.active_tooltips) { var tip = this.active_tooltips[tourName]; @@ -371,11 +374,23 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { }, _to_next_running_step: function (tip, tour_name) { if (this.running_tour !== tour_name) return; + var self = this; this._stop_running_tour_timeout(); + if (this.running_step_delay) { + // warning: due to the delay, it may happen that the $anchor isn't + // in the DOM anymore when exec is called, either because: + // - it has been removed from the DOM meanwhile and the tip's + // selector doesn't match anything anymore + // - it has been re-rendered and thus the selector still has a match + // in the DOM, but executing the step with that $anchor won't work + _.delay(exec, this.running_step_delay); + } else { + exec(); + } - var action_helper = new RunningTourActionHelper(tip.widget); - _.delay((function () { - do_before_unload(this._consume_tip.bind(this, tip, tour_name)); + function exec() { + var action_helper = new RunningTourActionHelper(tip.widget); + do_before_unload(self._consume_tip.bind(self, tip, tour_name)); if (typeof tip.run === "function") { tip.run.call(tip.widget, action_helper); @@ -385,7 +400,7 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { } else { action_helper.auto(); } - }).bind(this), this.running_step_delay); + } }, /** diff --git a/addons/web_tour/static/src/js/tour_service.js b/addons/web_tour/static/src/js/tour_service.js index 75e2b4daa8d..b0507d22e43 100644 --- a/addons/web_tour/static/src/js/tour_service.js +++ b/addons/web_tour/static/src/js/tour_service.js @@ -61,12 +61,8 @@ return session.is_bound.then(function () { }, 500); var observer = new MutationObserver(check_tooltip); var start_service = (function () { - var load_def; return function (observe) { - if (load_def === undefined && observe && session.is_frontend) { - load_def = ajax.loadXML('/web_tour/static/src/xml/tip.xml', QWeb); - } var def = $.Deferred(); $(function () { @@ -74,18 +70,16 @@ return session.is_bound.then(function () { * Once the DOM is ready, we still have to wait all the modules are loaded before completing the tours * registration and starting listening for DOM mutations. */ - $.when(load_def).then(function () { - _.defer(function () { - tour_manager._register_all(observe); - if (observe) { - observer.observe(document.body, { - attributes: true, - childList: true, - subtree: true, - }); - } - def.resolve(); - }); + _.defer(function () { + tour_manager._register_all(observe); + if (observe) { + observer.observe(document.body, { + attributes: true, + childList: true, + subtree: true, + }); + } + def.resolve(); }); }); return def; diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index d37852ae0de..a94397e77ba 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -74,7 +74,7 @@ class View(models.Model): view._create_website_specific_pages_for_view(website_specific_view, view.env['website'].browse(current_website_id)) - for inherit_child in view.inherit_children_ids.filter_duplicate(): + for inherit_child in view.inherit_children_ids.filter_duplicate().sorted(key=lambda v: (v.priority, v.id)): if inherit_child.website_id.id == current_website_id: # In the case the child was already specific to the current # website, we cannot just reattach it to the new specific @@ -93,6 +93,55 @@ class View(models.Model): return True @api.multi + def _get_specific_views(self): + """ Given a view, return a record set containing all the specific views + for that view's key. + If the given view is already specific, it will also return itself. + """ + self.ensure_one() + domain = [('key', '=', self.key), ('website_id', '!=', False)] + return self.with_context(active_test=False).search(domain) + + def _load_records_write(self, values): + """ During module update, when updating a generic view, we should also + update its specific views (COW'd). + Note that we will only update unmodified fields. That will mimic the + noupdate behavior on views having an ir.model.data. + """ + if self.type == 'qweb' and not self.website_id: + # Update also specific views + for cow_view in self._get_specific_views(): + authorized_vals = {} + for key in values: + if cow_view[key] == self[key]: + authorized_vals[key] = values[key] + cow_view.write(authorized_vals) + super(View, self)._load_records_write(values) + + def _load_records_create(self, values): + """ During module install, when creating a generic child view, we should + also create that view under specific view trees (COW'd). + Top level view (no inherit_id) do not need that behavior as they + will be shared between websites since there is no specific yet. + """ + records = super(View, self)._load_records_create(values) + for record in records: + if record.type == 'qweb' and record.inherit_id and not record.website_id and not record.inherit_id.website_id: + specific_parent_views = record.with_context(active_test=False).search([ + ('key', '=', record.inherit_id.key), + ('website_id', '!=', None), + ]) + for specific_parent_view in specific_parent_views: + record.copy({ + # Set key to avoid copy() to generate an unique key as + # we want the specific view to have the same key + 'key': record.key, + 'inherit_id': specific_parent_view.id, + 'website_id': specific_parent_view.website_id.id, + }) + return records + + @api.multi def unlink(self): '''This implements COU (copy-on-unlink). When deleting a generic page website-specific pages will be created so only the current diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 4aea35aa481..24099bef72d 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -1253,7 +1253,7 @@ class WebsiteRedirect(models.Model): _order = "sequence, id" _rec_name = 'url_from' - redirect_type = fields.Selection([('301', 'Moved permanently (301)'), ('302', 'Moved temporarily (302)')], string='Redirection Type', required=True, oldname='type') + redirect_type = fields.Selection([('301', 'Moved permanently (301)'), ('302', 'Moved temporarily (302)')], string='Redirection Type', required=True, default='301) url_from = fields.Char('Redirect From', required=True) url_to = fields.Char('Redirect To', required=True) website_id = fields.Many2one('website', 'Website') diff --git a/addons/website/static/src/js/menu/new_content.js b/addons/website/static/src/js/menu/new_content.js index 490e4f75516..2b4d1177a08 100644 --- a/addons/website/static/src/js/menu/new_content.js +++ b/addons/website/static/src/js/menu/new_content.js @@ -81,7 +81,7 @@ var NewContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ var $group = this.$dialog.find('div.form-group'); $group.removeClass('mb0'); - var $add = $('<div/>', {'class': 'form-group mb0'}) + var $add = $('<div/>', {'class': 'form-group mb0 row'}) .append($('<span/>', {'class': 'offset-md-3 col-md-9 text-left'}) .append(qweb.render('web_editor.components.switch', {id: 'switch_addTo_menu', label: _t("Add to menu")}))); $add.find('input').prop('checked', true); diff --git a/addons/website/static/src/scss/website.edit_mode.scss b/addons/website/static/src/scss/website.edit_mode.scss index de6f5d4c1a4..85ee8dc8fd4 100644 --- a/addons/website/static/src/scss/website.edit_mode.scss +++ b/addons/website/static/src/scss/website.edit_mode.scss @@ -176,8 +176,6 @@ font-family: Roboto, $font-family-sans-serif; .o_tooltip_container { - display: inline-flex; - align-items: center; transition: opacity 1s linear; > .o_tooltip { diff --git a/addons/website/static/src/xml/website.editor.xml b/addons/website/static/src/xml/website.editor.xml index 5018d28d1d9..292f624e403 100644 --- a/addons/website/static/src/xml/website.editor.xml +++ b/addons/website/static/src/xml/website.editor.xml @@ -3,7 +3,7 @@ <div t-name="website.homepage_editor_welcome_message" class="container text-center o_homepage_editor_welcome_message"> <h2 class="mt0">Welcome to your <b>Homepage</b>!</h2> <p class="lead d-none d-md-block">Let's start designing.</p> - <div class="o_tooltip_container d-none d-md-block fade">Follow all the <div class="o_tooltip bottom"/> signs to get your website ready in no time.</div> + <div class="o_tooltip_container d-none d-md-inline-flex fade">Follow all the <div class="o_tooltip bottom"/> signs to get your website ready in no time.</div> </div> <div t-name="website.leaving_current_page_edition"> <p>What do you want to do?</p> diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py index 132472de2c0..9ece6cf4c3d 100644 --- a/addons/website/tests/test_views.py +++ b/addons/website/tests/test_views.py @@ -622,6 +622,104 @@ class TestCowViewSaving(common.TransactionCase): views = View.with_context(website_id=1).get_related_views('B') self.assertEqual(views.mapped('key'), ['B', 'I', 'II'], "Should only return the specific tree") + def test_cow_inherit_children_order(self): + """ COW method should loop on inherit_children_ids in correct order + when copying them on the new specific tree. + Correct order is the same as the one when applying view arch: + PRIORITY, ID + And not the default one from ir.ui.view (NAME, PRIORIRTY, ID). + """ + self.inherit_view.copy({ + 'name': 'alphabetically before "Extension"', + 'key': '_test.alphabetically_first', + 'arch': '<div position="replace"><p>COMPARE</p></div>', + }) + # Next line should not crash, COW loop on inherit_children_ids should be sorted correctly + self.base_view.with_context(website_id=1).write({'name': 'Product (W1)'}) + + def test_module_new_inherit_view_on_parent_already_forked(self): + """ If a generic parent view is copied (COW) and that another module + creates a child view for that generic parent, all the COW views + should also get a copy of that new child view. + + Typically, a parent view (website_sale.product) is copied (COW) + and then wishlist module is installed. + Wishlist views inhering from website_sale.product are added to the + generic `website_sale.product`. But it should also be added to the + COW `website_sale.product` to activate the module views for that + website. + """ + Website = self.env['website'] + View = self.env['ir.ui.view'] + + # Simulate website_sale product view + self.base_view.write({'name': 'Product', 'key': '_website_sale.product'}) + # Trigger cow on website_sale hierarchy for website 1 + self.base_view.with_context(website_id=1).write({'name': 'Product (W1)'}) + + # Simulate website_sale_comparison install + View._load_records([dict(xml_id='_website_sale_comparison.product_add_to_compare', values={ + 'name': 'Add to comparison in product page', + 'mode': 'extension', + 'inherit_id': self.base_view.id, + 'arch': '<div position="replace"><p>COMPARE</p></div>', + 'key': '_website_sale_comparison.product_add_to_compare', + })]) + + specific_view = Website.with_context(load_all_views=True, website_id=1).viewref('_website_sale.product') + specific_view_arch = specific_view.read_combined(['arch'])['arch'] + self.assertEqual(specific_view.website_id.id, 1, "Ensure we got specific view to perform the checks against") + self.assertEqual(specific_view_arch, '<p>COMPARE</p>', "When a module creates an inherited view (on a generic tree), it should also create that view in the specific COW'd tree.") + + # Simulate website_sale_comparison update + View._load_records([dict(xml_id='_website_sale_comparison.product_add_to_compare', values={ + 'arch': '<div position="replace"><p>COMPARE EDITED</p></div>', + })]) + + specific_view_arch = Website.with_context(load_all_views=True, website_id=1).viewref('_website_sale.product').read_combined(['arch'])['arch'] + self.assertEqual(specific_view_arch, '<p>COMPARE EDITED</p>', "When a module updates an inherited view (on a generic tree), it should also update the copies of that view (COW).") + + # Test fields that should not be COW'd + random_view_id = View.search([], limit=1).id + View._load_records([dict(xml_id='_website_sale_comparison.product_add_to_compare', values={ + 'website_id': None, + 'inherit_id': random_view_id, + })]) + + w1_specific_child_view = Website.with_context(load_all_views=True, website_id=1).viewref('_website_sale_comparison.product_add_to_compare') + generic_child_view = Website.with_context(load_all_views=True).viewref('_website_sale_comparison.product_add_to_compare') + self.assertEqual(w1_specific_child_view.website_id.id, 1, "website_id is a prohibited field when COWing views during _load_records") + self.assertEqual(w1_specific_child_view.inherit_id.id != random_view_id, True, "inherit_id is a prohibited field when COWing views during _load_records") + self.assertEqual(generic_child_view.inherit_id.id, random_view_id, "prohibited fields only concerned write on COW'd view. Generic should still considere these fields") + + # Set back the generic view as parent for the rest of the test + generic_child_view.inherit_id = self.base_view + + # Don't update fields from COW'd view if these fields have been modified from original view + new_website = Website.create({'name': 'New Website'}) + self.base_view.with_context(website_id=new_website.id).write({'name': 'Product (new_website)'}) + new_website_specific_child_view = Website.with_context(load_all_views=True, website_id=new_website.id).viewref('_website_sale_comparison.product_add_to_compare') + new_website_specific_child_view.priority = 6 + View._load_records([dict(xml_id='_website_sale_comparison.product_add_to_compare', values={ + 'priority': 3, + })]) + self.assertEqual(generic_child_view.priority, 3, "XML update should be written on the Generic View") + self.assertEqual(w1_specific_child_view.priority, 3, "XML update should be written on the specific view if the fields have not been modified on that specific view") + self.assertEqual(new_website_specific_child_view.priority, 6, "XML update should NOT be written on the specific view if the fields have been modified on that specific view") + + # Simulate website_sale update on top level view + self.env['ir.model.data'].create({ + 'module': '_website_sale', + 'name': 'product', + 'model': self.base_view._name, + 'res_id': self.base_view.id, + }) + View._load_records([dict(xml_id='_website_sale.product', values={ + 'website_meta_title': 'A bug got fixed by updating this field', + })]) + all_title_updated = specific_view.website_meta_title == self.base_view.website_meta_title == "A bug got fixed by updating this field" + self.assertEqual(all_title_updated, True, "Update on top level generic views should also be applied on specific views") + class Crawler(HttpCase): def setUp(self): diff --git a/addons/website_forum/views/forum.xml b/addons/website_forum/views/forum.xml index 6a168613cdc..fe9188af31e 100644 --- a/addons/website_forum/views/forum.xml +++ b/addons/website_forum/views/forum.xml @@ -8,6 +8,19 @@ <menuitem name="Forum" id="menu_website_forum_global" parent="website.menu_website_global_configuration" sequence="170" groups="website.group_website_designer"/> + <record id="badge_form_view_forum" model="ir.ui.view"> + <field name="name">forum.badge_form_view</field> + <field name="model">gamification.badge</field> + <field name="inherit_id" ref="gamification.badge_form_view"/> + <field name="arch" type="xml"> + <field name="challenge_ids" position="after"> + <group> + <field name="level" /> + </group> + </field> + </field> + </record> + <!-- FORUM VIEWS --> <record id="view_forum_forum_list" model="ir.ui.view"> <field name="name">forum.forum.list</field> diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 0b5ed81a6e5..4f0f84cebf6 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -568,7 +568,7 @@ class WebsiteSale(ProductConfiguratorController): except ValidationError: error["vat"] = 'error' - if [err for err in error.items() if err == 'missing']: + if [err for err in error.values() if err == 'missing']: error_message.append(_('Some required fields are empty.')) return error, error_message diff --git a/addons/website_sale/static/src/js/website_sale_tour_buy.js b/addons/website_sale/static/src/js/website_sale_tour_buy.js index 1797a59ea9b..87ae9564b65 100644 --- a/addons/website_sale/static/src/js/website_sale_tour_buy.js +++ b/addons/website_sale/static/src/js/website_sale_tour_buy.js @@ -21,7 +21,7 @@ tour.register('shop_buy_product', { }, { content: "select conference chair", - trigger: '.oe_product_cart a:contains("Conference Chair")', + trigger: '.oe_product_cart:first a:contains("Conference Chair")', }, { content: "select Conference Chair Aluminium", diff --git a/addons/website_sale_comparison/controllers/main.py b/addons/website_sale_comparison/controllers/main.py index d58c6c5a0dc..9b833777b7b 100644 --- a/addons/website_sale_comparison/controllers/main.py +++ b/addons/website_sale_comparison/controllers/main.py @@ -3,6 +3,7 @@ from odoo import http, _ from odoo.http import request from odoo.addons.website_sale.controllers.main import WebsiteSale import json +from collections import OrderedDict class WebsiteSaleProductComparison(WebsiteSale): @@ -24,12 +25,12 @@ class WebsiteSaleProductComparison(WebsiteSale): att_name = var.attribute_id.name if not product.attribute_value_ids: # create_variant = False continue - res.setdefault(cat_name, {}).setdefault(att_name, [' - '] * len(products)) + res.setdefault(cat_name, OrderedDict()).setdefault(att_name, [' - '] * len(products)) val = product.attribute_value_ids.filtered(lambda x: x.attribute_id == var.attribute_id) if val: res[cat_name][att_name][num] = val[0].name values['specs'] = res - values['compute_currency'] = self._get_compute_currency_and_context(products[:1])[0] + values['compute_currency'] = self._get_compute_currency_and_context(products[:1].product_tmpl_id)[0] return request.render("website_sale_comparison.product_compare", values) @http.route(['/shop/get_product_data'], type='json', auth="public", website=True) @@ -37,7 +38,7 @@ class WebsiteSaleProductComparison(WebsiteSale): ret = {} pricelist_context, pricelist = self._get_pricelist_context() prods = request.env['product.product'].with_context(pricelist_context, display_default_code=False).search([('id', 'in', product_ids)]) - compute_currency = self._get_compute_currency(pricelist, prods[:1]) + compute_currency = self._get_compute_currency(pricelist, prods[:1].product_tmpl_id) if cookies is not None: ret['cookies'] = json.dumps(request.env['product.product'].search([('id', 'in', list(set(product_ids + cookies)))]).ids) diff --git a/addons/website_sale_wishlist/static/src/js/website_sale_tour_wishlist.js b/addons/website_sale_wishlist/static/src/js/website_sale_tour_wishlist.js index b03e0108fb2..8089d8fb3e7 100644 --- a/addons/website_sale_wishlist/static/src/js/website_sale_tour_wishlist.js +++ b/addons/website_sale_wishlist/static/src/js/website_sale_tour_wishlist.js @@ -47,14 +47,9 @@ tour.register('shop_wishlist', { }, }, { - content: "check that logged in and search for Customizable Desk", - extra_trigger: "li span:contains('Mitchell Admin')", - trigger: 'form input[name="search"]', - run: "text Customizable Desk", - }, - { - content: "submit search", - trigger: 'form:has(input[name="search"]) .oe_search_button', + content: "check that logged in", + trigger: "li span:contains('Mitchell Admin')", + run: function () {}, }, { content: "click on Customizable Desk", diff --git a/addons/website_slides/views/website_slides_embed_templates.xml b/addons/website_slides/views/website_slides_embed_templates.xml index 55f63c1d3e1..c37facd252e 100644 --- a/addons/website_slides/views/website_slides_embed_templates.xml +++ b/addons/website_slides/views/website_slides_embed_templates.xml @@ -113,7 +113,7 @@ </a> </div> <div class="col-auto text-right ml-auto mt-2 mb-2"> - <a t-if="slide.download_security and not is_embedded" target="_top" t-attf-href="/slides/slide/#{slug(slide)}/download" title="Download" id="download"> + <a t-if="slide.download_security != 'none' and not is_embedded" target="_top" t-attf-href="/slides/slide/#{slug(slide)}/download" title="Download" id="download"> <i class="fa fa-download fa-2x" role="img" aria-label="Download"></i> </a> <span id="fullscreen" title="View fullscreen" role="img" aria-label="Fullscreen"> diff --git a/doc/cla/corporate/merchise.md b/doc/cla/corporate/merchise.md index e4cbd3c3f95..64d5b277ccd 100644 --- a/doc/cla/corporate/merchise.md +++ b/doc/cla/corporate/merchise.md @@ -21,3 +21,4 @@ Medardo Rodríguez med@merchise.org https://github.com/med-merchise Abel Firvida firvida@merchise.org Abel Firvida abel@merchise.org Mónica Díaz Pena monicadp@merchise.org https://github.com/mdpena +Larisa González larisa@merchise.org diff --git a/doc/cla/individual/dipsshah42.md b/doc/cla/individual/dipsshah42.md new file mode 100644 index 00000000000..d6a647178ae --- /dev/null +++ b/doc/cla/individual/dipsshah42.md @@ -0,0 +1,9 @@ +India, 2019-01-11 + +I hereby agree to the terms of the Odoo Individual Contributor License Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this declaration. + +Signed, + +Dipak Shah dipssah@gmail.com https://github.com/dipsshah42 diff --git a/doc/cla/individual/ehansejoerg.md b/doc/cla/individual/ehansejoerg.md new file mode 100644 index 00000000000..f4be23d6c5c --- /dev/null +++ b/doc/cla/individual/ehansejoerg.md @@ -0,0 +1,11 @@ +Germany, 10.01.2019 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Jörg Ricardo Schumacher eHanseJoerg@users.noreply.github.com https://github.com/eHanseJoerg diff --git a/doc/cla/individual/i-vyshnevska.md b/doc/cla/individual/i-vyshnevska.md new file mode 100644 index 00000000000..0df4e78944d --- /dev/null +++ b/doc/cla/individual/i-vyshnevska.md @@ -0,0 +1,9 @@ +Ukraine, 2019-01-09 + +I hereby agree to the terms of the Odoo Individual Contributor License Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this declaration. + +Signed, + +Vyshnevska Iryna i.vyshnevska@mobilunity.com https://github.com/i-vyshnevska/
\ No newline at end of file diff --git a/doc/cla/individual/stanisljevic.md b/doc/cla/individual/stanisljevic.md new file mode 100644 index 00000000000..16d61d63d19 --- /dev/null +++ b/doc/cla/individual/stanisljevic.md @@ -0,0 +1,11 @@ +Bosnia and Herzegovina, 2019-01-12 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Sasa Stanisljevic mcse.sasa@gmail.com https://github.com/stanisljevic diff --git a/odoo/addons/base/models/ir_actions.py b/odoo/addons/base/models/ir_actions.py index 2b9f88104f1..80c61aab9d7 100644 --- a/odoo/addons/base/models/ir_actions.py +++ b/odoo/addons/base/models/ir_actions.py @@ -397,7 +397,7 @@ class IrActionsServer(models.Model): # Create crud_model_id = fields.Many2one('ir.model', string='Create/Write Target Model', oldname='srcmodel_id', help="Model for record creation / update. Set this field only to specify a different model than the base model.") - crud_model_name = fields.Char(related='crud_model_id.name', readonly=True) + crud_model_name = fields.Char(related='crud_model_id.model', string='Target Model', readonly=True) link_field_id = fields.Many2one('ir.model.fields', string='Link using field', help="Provide the field used to link the newly created record " "on the record on used by the server action.") diff --git a/odoo/addons/base/models/ir_module.py b/odoo/addons/base/models/ir_module.py index e673ac016dd..0e5fd313ab9 100644 --- a/odoo/addons/base/models/ir_module.py +++ b/odoo/addons/base/models/ir_module.py @@ -457,7 +457,8 @@ class Module(models.Model): modules_to_remove = self.mapped('name') self.env['ir.model.data']._module_data_uninstall(modules_to_remove) self._remove_copied_views() - self.write({'state': 'uninstalled', 'latest_version': False}) + # we deactivate prefetching to not try to read a column that has been deleted + self.with_context(prefetch_fields=False).write({'state': 'uninstalled', 'latest_version': False}) return True @api.multi diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py index 8f1bb377c99..d8b949c2fc2 100644 --- a/odoo/addons/base/models/res_partner.py +++ b/odoo/addons/base/models/res_partner.py @@ -257,10 +257,10 @@ class Partner(models.Model): for partner in self: partner.tz_offset = datetime.datetime.now(pytz.timezone(partner.tz or 'GMT')).strftime('%z') - @api.depends('user_ids.share') + @api.depends('user_ids.share', 'user_ids.active') def _compute_partner_share(self): for partner in self: - partner.partner_share = not partner.user_ids or any(user.share for user in partner.user_ids) + partner.partner_share = not partner.user_ids or not any(not user.share for user in partner.user_ids) @api.depends(lambda self: self._display_address_depends()) def _compute_contact_address(self): diff --git a/odoo/addons/base/wizard/base_partner_merge.py b/odoo/addons/base/wizard/base_partner_merge.py index cce25eed51f..7a8323f67e1 100644 --- a/odoo/addons/base/wizard/base_partner_merge.py +++ b/odoo/addons/base/wizard/base_partner_merge.py @@ -129,14 +129,14 @@ class MergePartnerAutomatic(models.TransientModel): # unique key treated query = """ UPDATE "%(table)s" as ___tu - SET %(column)s = %%s + SET "%(column)s" = %%s WHERE - %(column)s = %%s AND + "%(column)s" = %%s AND NOT EXISTS ( SELECT 1 FROM "%(table)s" as ___tw WHERE - %(column)s = %%s AND + "%(column)s" = %%s AND ___tu.%(value)s = ___tw.%(value)s )""" % query_dic for partner in src_partners: @@ -144,7 +144,7 @@ class MergePartnerAutomatic(models.TransientModel): else: try: with mute_logger('odoo.sql_db'), self._cr.savepoint(): - query = 'UPDATE "%(table)s" SET %(column)s = %%s WHERE %(column)s IN %%s' % query_dic + query = 'UPDATE "%(table)s" SET "%(column)s" = %%s WHERE "%(column)s" IN %%s' % query_dic self._cr.execute(query, (dst_partner.id, tuple(src_partners.ids),)) # handle the recursivity with parent relation diff --git a/odoo/addons/test_main_flows/static/src/js/tour.js b/odoo/addons/test_main_flows/static/src/js/tour.js index 1e65a2b2e58..af051f956e1 100644 --- a/odoo/addons/test_main_flows/static/src/js/tour.js +++ b/odoo/addons/test_main_flows/static/src/js/tour.js @@ -211,7 +211,6 @@ tour.register('main_flow_tour', { position: 'bottom', }, { trigger: ".breadcrumb-item:first", - extra_trigger: ".o_form_readonly", // FIXME: this is required due to an issue in tour_manager (see [*]) content: _t("Use the breadcrumbs to <b>go back to products</b>."), position: "bottom" }, { @@ -542,12 +541,10 @@ tour.register('main_flow_tour', { trigger: ".o_menu_sections a[data-menu-xmlid='mrp.menu_mrp_manufacturing']", content: _t('Click on Operations menuitem'), position: 'bottom', - edition: 'enterprise', }, { trigger: ".o_menu_sections a[data-menu-xmlid='mrp.menu_mrp_production_action']", content: _t('Open manufacturing orders'), position: 'bottom', - edition: 'enterprise', }, { trigger: '.o_data_row:has(.o_data_cell:contains("the_flow.product")):first', content: _t('Select the generated manufacturing order'), @@ -755,7 +752,6 @@ tour.register('main_flow_tour', { }, { edition: "enterprise", trigger: ".o_selected_row .o_field_widget[name=name]", - extra_trigger: ".o_selected_row .o_field_widget[name=partner_id] .o_external_button", // FIXME: this is required due to an issue in tour_manager (see [*]) content: _t('Let\'s enter a name.'), position: "bottom", run: "text the_flow.statement.line", @@ -781,16 +777,3 @@ tour.register('main_flow_tour', { position: "bottom", }]); }); - -/* - * [*] FIXME: issue in tour_manager: - * The JQuery element of a step is registered as soon as its trigger and - * extra_trigger elements are visible in the DOM, but it's action is delayed - * to handle the 'running_step_delay' option (0 by default, but even with 0, - * the action is executed after finishing the current execution block, which - * may produce a re-rendering of the part of the DOM containing the element). - * When this happens, the action is executed on an jquery element not present - * in the DOM anymore. - * To avoid this, we add an 'extra_trigger' to wait for the updated jquery - * element before activating the step (and thus registering the JQuery element). - */ diff --git a/odoo/addons/test_new_api/ir.model.access.csv b/odoo/addons/test_new_api/ir.model.access.csv index 77c881dcc4d..77e0b8e165b 100644 --- a/odoo/addons/test_new_api/ir.model.access.csv +++ b/odoo/addons/test_new_api/ir.model.access.csv @@ -26,3 +26,4 @@ access_test_new_api_monetary_base,access_test_new_api_monetary_base,model_test_n access_test_new_api_monetary_related,access_test_new_api_monetary_related,model_test_new_api_monetary_related,,1,1,1,1 access_test_new_api_monetary_custom,access_test_new_api_monetary_custom,model_test_new_api_monetary_custom,,1,1,1,1 access_test_new_api_monetary_inherits,access_test_new_api_monetary_inherits,model_test_new_api_monetary_inherits,,1,1,1,1 +access_test_new_api_field_with_caps,access_test_new_api_field_with_caps,model_test_new_api_field_with_caps,,1,1,1,1 diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py index 90edf910944..6bd198006ee 100644 --- a/odoo/addons/test_new_api/models.py +++ b/odoo/addons/test_new_api/models.py @@ -504,3 +504,10 @@ class MonetaryInherits(models.Model): monetary_id = fields.Many2one('test_new_api.monetary_base', required=True, ondelete='cascade') currency_id = fields.Many2one('res.currency') + + +class FieldWithCaps(models.Model): + _name = 'test_new_api.field_with_caps' + _description = 'Model with field defined with capital letters' + + pArTneR_321_id = fields.Many2one('res.partner') diff --git a/odoo/addons/test_new_api/tests/test_one2many.py b/odoo/addons/test_new_api/tests/test_one2many.py index 8531396fe48..62de71c07fc 100644 --- a/odoo/addons/test_new_api/tests/test_one2many.py +++ b/odoo/addons/test_new_api/tests/test_one2many.py @@ -130,3 +130,22 @@ class One2manyCase(TransactionCase): res_movies_not_of_edition_name = self.Movie.search([('editions', '!=', one_movie_edition.name)]) self.assertItemsEqual(t(res_movies_not_of_edition_name), t(movies.filtered(lambda r: one_movie_edition not in r.editions))) + + def test_merge_partner(self): + model = self.env['test_new_api.field_with_caps'] + partner = self.env['res.partner'] + + p1 = partner.create({'name': 'test1'}) + p2 = partner.create({'name': 'test2'}) + + model1 = model.create({'pArTneR_321_id': p1.id}) + model2 = model.create({'pArTneR_321_id': p2.id}) + + self.env['base.partner.merge.automatic.wizard']._merge((p1 + p2).ids, p1) + + self.assertFalse(p2.exists()) + self.assertTrue(p1.exists()) + + self.assertEqual(model1.pArTneR_321_id, p1) + self.assertTrue(model2.exists()) + self.assertEqual(model2.pArTneR_321_id, p1) diff --git a/odoo/models.py b/odoo/models.py index eca249f6200..9104406195b 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -2778,14 +2778,16 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # retrieve results from records; this takes values from the cache and # computes remaining fields + self = self.with_prefetch(self._prefetch.copy()) data = {record: {'id': record.id} for record in self} missing = set() use_name_get = (load == '_classic_read') for name in (stored + inherited + computed): convert = self._fields[name].convert_to_read - # read every field with prefetching limited to self; this avoids + # restrict the prefetching of self's model to self; this avoids # computing fields on a larger recordset than self - for record in self.with_prefetch(): + self._prefetch[self._name] = set(self._ids) + for record in self: try: data[record][name] = convert(record[name], record, use_name_get) except MissingError: @@ -3738,6 +3740,12 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): modified_ids = {row[0] for row in cr.fetchall()} self.browse(modified_ids).modified(['parent_path']) + def _load_records_write(self, values): + self.write(values) + + def _load_records_create(self, values): + return self.create(values) + def _load_records(self, data_list, update=False): """ Create or update records of this model, and assign XMLIDs. @@ -3795,7 +3803,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # update existing records for data in to_update: - data['record'].write(data['values']) + data['record']._load_records_write(data['values']) # determine existing parents for new records for parent_model, parent_field in self._inherits.items(): @@ -3822,7 +3830,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): _logger.warning("Creating record %s in module %s.", data['xml_id'], module) # create records - records = self.create([data['values'] for data in to_create]) + records = self._load_records_create([data['values'] for data in to_create]) for data, record in zip(to_create, records): data['record'] = record diff --git a/odoo/modules/module.py b/odoo/modules/module.py index c94df025f9b..378812f1036 100644 --- a/odoo/modules/module.py +++ b/odoo/modules/module.py @@ -128,17 +128,17 @@ def initialize_sys_path(): global ad_paths global hooked - dd = tools.config.addons_data_dir + dd = os.path.normcase(tools.config.addons_data_dir) if os.access(dd, os.R_OK) and dd not in ad_paths: ad_paths.append(dd) for ad in tools.config['addons_path'].split(','): - ad = os.path.abspath(tools.ustr(ad.strip())) + ad = os.path.normcase(os.path.abspath(tools.ustr(ad.strip()))) if ad not in ad_paths: ad_paths.append(ad) # add base module path - base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons')) + base_path = os.path.normcase(os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons'))) if base_path not in ad_paths and os.path.isdir(base_path): ad_paths.append(base_path) diff --git a/odoo/tests/common.py b/odoo/tests/common.py index 9a03f589200..51660dca0a8 100644 --- a/odoo/tests/common.py +++ b/odoo/tests/common.py @@ -529,6 +529,7 @@ class ChromeBrowser(): '--user-data-dir': self.user_data_dir, '--disable-translate': '', '--window-size': self.window_size, + '--remote-debugging-address': HOST, '--remote-debugging-port': str(self.devtools_port), '--no-sandbox': '', } @@ -571,7 +572,7 @@ class ChromeBrowser(): command = os.path.join('json', command).strip('/') while timeout > 0: try: - url = werkzeug.urls.url_join('http://127.0.0.1:%s/' % self.devtools_port, command) + url = werkzeug.urls.url_join('http://%s:%s/' % (HOST, self.devtools_port), command) self._logger.info('Url : %s', url) r = requests.get(url, timeout=3) if r.ok: @@ -881,7 +882,7 @@ class HttpCase(TransactionCase): odoo.http.root.session_store.save(session) if self.browser: self._logger.info('Setting session cookie in browser') - self.browser.set_cookie('session_id', self.session_id, '/', '127.0.0.1') + self.browser.set_cookie('session_id', self.session_id, '/', HOST) def browser_js(self, url_path, code, ready='', login=None, timeout=60, **kw): """ Test js code running in the browser @@ -905,7 +906,10 @@ class HttpCase(TransactionCase): try: self.authenticate(login, login) - url = "http://%s:%s%s" % (HOST, PORT, url_path or '/') + base_url = "http://%s:%s" % (HOST, PORT) + ICP = self.env['ir.config_parameter'] + ICP.set_param('web.base.url', base_url) + url = "%s%s" % (base_url, url_path or '/') self._logger.info('Open "%s" in browser', url) if odoo.tools.config['logfile']: |
