diff options
65 files changed, 499 insertions, 247 deletions
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 23b1a849809..db16d61e24e 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -39,7 +39,7 @@ class AccountInvoice(models.Model): _order = "date_invoice desc, number desc, id desc" @api.one - @api.depends('invoice_line_ids.price_subtotal', 'tax_line_ids.amount', 'currency_id', 'company_id') + @api.depends('invoice_line_ids.price_subtotal', 'tax_line_ids.amount', 'currency_id', 'company_id', 'date_invoice') def _compute_amount(self): self.amount_untaxed = sum(line.price_subtotal for line in self.invoice_line_ids) self.amount_tax = sum(line.amount for line in self.tax_line_ids) @@ -47,8 +47,9 @@ class AccountInvoice(models.Model): amount_total_company_signed = self.amount_total amount_untaxed_signed = self.amount_untaxed if self.currency_id and self.currency_id != self.company_id.currency_id: - amount_total_company_signed = self.currency_id.compute(self.amount_total, self.company_id.currency_id) - amount_untaxed_signed = self.currency_id.compute(self.amount_untaxed, self.company_id.currency_id) + currency_id = self.currency_id.with_context(date=self.date_invoice) + amount_total_company_signed = currency_id.compute(self.amount_total, self.company_id.currency_id) + amount_untaxed_signed = currency_id.compute(self.amount_untaxed, self.company_id.currency_id) sign = self.type in ['in_refund', 'out_refund'] and -1 or 1 self.amount_total_company_signed = amount_total_company_signed * sign self.amount_total_signed = self.amount_total * sign diff --git a/addons/account/views/report_agedpartnerbalance.xml b/addons/account/views/report_agedpartnerbalance.xml index 8681931febb..8fa10c8df90 100644 --- a/addons/account/views/report_agedpartnerbalance.xml +++ b/addons/account/views/report_agedpartnerbalance.xml @@ -4,7 +4,7 @@ <t t-call="report.html_container"> <t t-call="report.internal_layout"> <div class="page"> - <h2>Aged Trial Balance</h2> + <h2>Aged Partner Balance</h2> <div class="row mt32"> <div class="col-xs-3"> diff --git a/addons/account_asset/models/account_invoice.py b/addons/account_asset/models/account_invoice.py index 9eb4cca484b..91e6336d23c 100644 --- a/addons/account_asset/models/account_invoice.py +++ b/addons/account_asset/models/account_invoice.py @@ -28,8 +28,8 @@ class AccountInvoiceLine(models.Model): _inherit = 'account.invoice.line' asset_category_id = fields.Many2one('account.asset.category', string='Asset Category') - asset_start_date = fields.Date(string='Asset End Date', compute='_get_asset_date', readonly=True, store=True) - asset_end_date = fields.Date(string='Asset Start Date', compute='_get_asset_date', readonly=True, store=True) + asset_start_date = fields.Date(string='Asset Start Date', compute='_get_asset_date', readonly=True, store=True) + asset_end_date = fields.Date(string='Asset End Date', compute='_get_asset_date', readonly=True, store=True) asset_mrr = fields.Float(string='Monthly Recurring Revenue', compute='_get_asset_date', readonly=True, digits=dp.get_precision('Account'), store=True) @api.one diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index bd65166fd65..d3e1a71555b 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -916,10 +916,10 @@ class Meeting(models.Model): meeting_attendees |= attendee meeting_partners |= partner - if current_user.email != partner.email: - attendee._send_mail_to_attendees('calendar.calendar_template_meeting_invitation') - if meeting_attendees: + to_notify = meeting_attendees.filtered(lambda a: a.email != current_user.email) + to_notify._send_mail_to_attendees('calendar.calendar_template_meeting_invitation') + meeting.write({'attendee_ids': [(4, meeting_attendee.id) for meeting_attendee in meeting_attendees]}) if meeting_partners: meeting.message_subscribe(partner_ids=meeting_partners.ids) diff --git a/addons/event/models/event_mail.py b/addons/event/models/event_mail.py index a17635efd7d..6a5edd12854 100644 --- a/addons/event/models/event_mail.py +++ b/addons/event/models/event_mail.py @@ -71,7 +71,8 @@ class EventMailScheduler(models.Model): if self.interval_type == 'after_sub': # update registration lines lines = [] - for registration in filter(lambda item: item not in [mail_reg.registration_id for mail_reg in self.mail_registration_ids], self.event_id.registration_ids): + reg_ids = [mail_reg.registration_id for mail_reg in self.mail_registration_ids] + for registration in filter(lambda item: item not in reg_ids, self.event_id.registration_ids): lines.append((0, 0, {'registration_id': registration.id})) if lines: self.write({'mail_registration_ids': lines}) diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 84886443a37..a6b1364308d 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -254,7 +254,7 @@ class HrExpense(models.Model): # Calculate tax lines and adjust base line taxes = expense.tax_ids.compute_all(expense.unit_amount, expense.currency_id, expense.quantity, expense.product_id) account_move[-1]['price'] = taxes['total_excluded'] - account_move[-1]['tax_ids'] = expense.tax_ids.id + account_move[-1]['tax_ids'] = expense.tax_ids.ids for tax in taxes['taxes']: account_move.append({ 'type': 'tax', diff --git a/addons/l10n_de_skr03/data/l10n_de_skr03_chart_data.xml b/addons/l10n_de_skr03/data/l10n_de_skr03_chart_data.xml index 368cd9a5937..d725d573070 100644 --- a/addons/l10n_de_skr03/data/l10n_de_skr03_chart_data.xml +++ b/addons/l10n_de_skr03/data/l10n_de_skr03_chart_data.xml @@ -484,7 +484,7 @@ <record model="account.account.template" id="account_1120"> <field name="name">Postbank 2</field> <field name="code">1120</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -708,7 +708,7 @@ <record model="account.account.template" id="account_1010"> <field name="name">Nebenkasse 1</field> <field name="code">1010</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -750,14 +750,14 @@ <record model="account.account.template" id="account_1130"> <field name="name">Postbank 3</field> <field name="code">1130</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> <record model="account.account.template" id="account_1230"> <field name="name">Bank 3</field> <field name="code">1230</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -806,7 +806,7 @@ <record model="account.account.template" id="account_1330"> <field name="name">Schecks</field> <field name="code">1330</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -925,7 +925,7 @@ <record model="account.account.template" id="account_1240"> <field name="name">Bank 4</field> <field name="code">1240</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -1492,7 +1492,7 @@ <record model="account.account.template" id="account_1190"> <field name="name">LZB-Guthaben</field> <field name="code">1190</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -2850,7 +2850,7 @@ <record model="account.account.template" id="account_1220"> <field name="name">Bank 2</field> <field name="code">1220</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -3116,7 +3116,7 @@ <record model="account.account.template" id="account_1110"> <field name="name">Postbank 1</field> <field name="code">1110</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -4019,7 +4019,7 @@ <record model="account.account.template" id="account_1020"> <field name="name">Nebenkasse 2</field> <field name="code">1020</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -4796,7 +4796,7 @@ <record model="account.account.template" id="account_1210"> <field name="name">Bank 1</field> <field name="code">1210</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -5020,7 +5020,7 @@ <record model="account.account.template" id="account_1100"> <field name="name">Postbank</field> <field name="code">1100</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -6161,7 +6161,7 @@ <record model="account.account.template" id="account_1250"> <field name="name">Bank 5</field> <field name="code">1250</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -6511,7 +6511,7 @@ <record model="account.account.template" id="account_1195"> <field name="name">Bundesbankguthaben</field> <field name="code">1195</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> @@ -7218,7 +7218,7 @@ <record model="account.account.template" id="account_1360"> <field name="name">Geldtransit</field> <field name="code">1360</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_de_chart_template"/> </record> diff --git a/addons/l10n_de_skr04/data/l10n_de_skr04_chart_data.xml b/addons/l10n_de_skr04/data/l10n_de_skr04_chart_data.xml index 81b0d42574f..fb9f9bf758d 100644 --- a/addons/l10n_de_skr04/data/l10n_de_skr04_chart_data.xml +++ b/addons/l10n_de_skr04/data/l10n_de_skr04_chart_data.xml @@ -3463,7 +3463,7 @@ <record id="chart_skr04_1460" model="account.account.template"> <field name="code">1460</field> <field name="name">Geldtransit</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3615,7 +3615,7 @@ <record id="chart_skr04_1610" model="account.account.template"> <field name="code">1610</field> <field name="name">Nebenkasse 1</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3623,7 +3623,7 @@ <record id="chart_skr04_1620" model="account.account.template"> <field name="code">1620</field> <field name="name">Nebenkasse 2</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3631,7 +3631,7 @@ <record id="chart_skr04_1700" model="account.account.template"> <field name="code">1700</field> <field name="name">Postbank</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3639,7 +3639,7 @@ <record id="chart_skr04_1710" model="account.account.template"> <field name="code">1710</field> <field name="name">Postbank 1</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3647,7 +3647,7 @@ <record id="chart_skr04_1720" model="account.account.template"> <field name="code">1720</field> <field name="name">Postbank 2</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3655,7 +3655,7 @@ <record id="chart_skr04_1730" model="account.account.template"> <field name="code">1730</field> <field name="name">Postbank 3</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3679,7 +3679,7 @@ <record id="chart_skr04_1810" model="account.account.template"> <field name="code">1810</field> <field name="name">Bank 1</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3687,7 +3687,7 @@ <record id="chart_skr04_1820" model="account.account.template"> <field name="code">1820</field> <field name="name">Bank 2</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3695,7 +3695,7 @@ <record id="chart_skr04_1830" model="account.account.template"> <field name="code">1830</field> <field name="name">Bank 3</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3703,7 +3703,7 @@ <record id="chart_skr04_1840" model="account.account.template"> <field name="code">1840</field> <field name="name">Bank 4</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> @@ -3711,7 +3711,7 @@ <record id="chart_skr04_1850" model="account.account.template"> <field name="code">1850</field> <field name="name">Bank 5</field> - <field name="reconcile" eval="True"/> + <field name="reconcile" eval="False"/> <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="chart_template_id" ref="l10n_chart_de_skr04"/> </record> diff --git a/addons/l10n_fr/data/account_fiscal_position_template_data.xml b/addons/l10n_fr/data/account_fiscal_position_template_data.xml index 68be1323c2c..51108bb0be8 100644 --- a/addons/l10n_fr/data/account_fiscal_position_template_data.xml +++ b/addons/l10n_fr/data/account_fiscal_position_template_data.xml @@ -38,30 +38,55 @@ <field name="tax_src_id" ref="tva_normale" /> <field name="tax_dest_id" ref="tva_intra_0" /> </record> + <record id="fp_tax_template_intraeub2b_vt_normale_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_intraeub2b" /> + <field name="tax_src_id" ref="tva_normale_ttc" /> + <field name="tax_dest_id" ref="tva_intra_0" /> + </record> <!-- Taux DOM-TOM --> <record id="fp_tax_template_intraeub2b_vt_specifique" model="account.fiscal.position.tax.template"> <field name="position_id" ref="fiscal_position_template_intraeub2b" /> <field name="tax_src_id" ref="tva_specifique" /> <field name="tax_dest_id" ref="tva_intra_0" /> </record> + <record id="fp_tax_template_intraeub2b_vt_specifique_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_intraeub2b" /> + <field name="tax_src_id" ref="tva_specifique_ttc" /> + <field name="tax_dest_id" ref="tva_intra_0" /> + </record> <!-- Taux Intermédiaire --> <record id="fp_tax_template_intraeub2b_vt_intermediaire" model="account.fiscal.position.tax.template"> <field name="position_id" ref="fiscal_position_template_intraeub2b" /> <field name="tax_src_id" ref="tva_intermediaire" /> <field name="tax_dest_id" ref="tva_intra_0" /> </record> + <record id="fp_tax_template_intraeub2b_vt_intermediaire_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_intraeub2b" /> + <field name="tax_src_id" ref="tva_intermediaire_ttc" /> + <field name="tax_dest_id" ref="tva_intra_0" /> + </record> <!-- Taux réduit --> <record id="fp_tax_template_intraeub2b_vt_reduite" model="account.fiscal.position.tax.template"> <field name="position_id" ref="fiscal_position_template_intraeub2b" /> <field name="tax_src_id" ref="tva_reduite" /> <field name="tax_dest_id" ref="tva_intra_0" /> </record> + <record id="fp_tax_template_intraeub2b_vt_reduite_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_intraeub2b" /> + <field name="tax_src_id" ref="tva_reduite_ttc" /> + <field name="tax_dest_id" ref="tva_intra_0" /> + </record> <!-- Taux super réduit --> <record id="fp_tax_template_intraeub2b_vt_super_reduite" model="account.fiscal.position.tax.template"> <field name="position_id" ref="fiscal_position_template_intraeub2b" /> <field name="tax_src_id" ref="tva_super_reduite" /> <field name="tax_dest_id" ref="tva_intra_0" /> </record> + <record id="fp_tax_template_intraeub2b_vt_super_reduite_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_intraeub2b" /> + <field name="tax_src_id" ref="tva_super_reduite_ttc" /> + <field name="tax_dest_id" ref="tva_intra_0" /> + </record> <!-- achats --> <!-- Taux Normal --> <record id="fp_tax_template_intraeub2b_ha_normale_deduc" model="account.fiscal.position.tax.template"> @@ -127,30 +152,55 @@ <field name="tax_src_id" ref="tva_normale" /> <field name="tax_dest_id" ref="tva_export_0" /> </record> + <record id="fp_tax_template_impexp_vt_normale_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_import_export" /> + <field name="tax_src_id" ref="tva_normale_ttc" /> + <field name="tax_dest_id" ref="tva_export_0" /> + </record> <!-- Taux DOM-TOM --> <record id="fp_tax_template_impexp_vt_specifique" model="account.fiscal.position.tax.template"> <field name="position_id" ref="fiscal_position_template_import_export" /> <field name="tax_src_id" ref="tva_specifique" /> <field name="tax_dest_id" ref="tva_export_0" /> </record> + <record id="fp_tax_template_impexp_vt_specifique_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_import_export" /> + <field name="tax_src_id" ref="tva_specifique_ttc" /> + <field name="tax_dest_id" ref="tva_export_0" /> + </record> <!-- Taux Intermédiare --> <record id="fp_tax_template_impexp_vt_intermediaire" model="account.fiscal.position.tax.template"> <field name="position_id" ref="fiscal_position_template_import_export" /> <field name="tax_src_id" ref="tva_intermediaire" /> <field name="tax_dest_id" ref="tva_export_0" /> </record> + <record id="fp_tax_template_impexp_vt_intermediaire_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_import_export" /> + <field name="tax_src_id" ref="tva_intermediaire_ttc" /> + <field name="tax_dest_id" ref="tva_export_0" /> + </record> <!-- Taux Réduit --> <record id="fp_tax_template_impexp_vt_reduite" model="account.fiscal.position.tax.template"> <field name="position_id" ref="fiscal_position_template_import_export" /> <field name="tax_src_id" ref="tva_reduite" /> <field name="tax_dest_id" ref="tva_export_0" /> </record> + <record id="fp_tax_template_impexp_vt_reduite_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_import_export" /> + <field name="tax_src_id" ref="tva_reduite_ttc" /> + <field name="tax_dest_id" ref="tva_export_0" /> + </record> <!-- Taux super réduit --> <record id="fp_tax_template_impexp_vt_super_reduite" model="account.fiscal.position.tax.template"> <field name="position_id" ref="fiscal_position_template_import_export" /> <field name="tax_src_id" ref="tva_super_reduite" /> <field name="tax_dest_id" ref="tva_export_0" /> </record> + <record id="fp_tax_template_impexp_vt_super_reduite_ttc" model="account.fiscal.position.tax.template"> + <field name="position_id" ref="fiscal_position_template_import_export" /> + <field name="tax_src_id" ref="tva_super_reduite_ttc" /> + <field name="tax_dest_id" ref="tva_export_0" /> + </record> <!-- achats --> <!-- Taux Normal --> diff --git a/addons/l10n_fr/data/account_tax_data.xml b/addons/l10n_fr/data/account_tax_data.xml index 7778e6ef82d..1d14f61cae1 100644 --- a/addons/l10n_fr/data/account_tax_data.xml +++ b/addons/l10n_fr/data/account_tax_data.xml @@ -32,6 +32,31 @@ <field name="applicability">taxes</field> </record> + <record id="tax_20_0_TTC" model="account.account.tag"> + <field name="name">20.0-TTC</field> + <field name="applicability">taxes</field> + </record> + + <record id="tax_10_0_TTC" model="account.account.tag"> + <field name="name">10.0-TTC</field> + <field name="applicability">taxes</field> + </record> + + <record id="tax_8_5_TTC" model="account.account.tag"> + <field name="name">8.5-TTC</field> + <field name="applicability">taxes</field> + </record> + + <record id="tax_5_5_TTC" model="account.account.tag"> + <field name="name">5.5-TTC</field> + <field name="applicability">taxes</field> + </record> + + <record id="tax_2_1_TTC" model="account.account.tag"> + <field name="name">2.1-TTC</field> + <field name="applicability">taxes</field> + </record> + <record id="tax_ACH-8_5" model="account.account.tag"> <field name="name">ACH-8_5</field> <field name="applicability">taxes</field> @@ -249,6 +274,77 @@ </record> +<!-- VENTES Tax INCLUDE (TTC) --> + <record model="account.tax.template" id="tva_normale_ttc"> + <field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/> + <field name="name">TVA collectée (vente) 20,0% TTC</field> + <field name="description">20.0-TTC</field> + <field name="price_include" eval="1"/> + <field name="amount" eval="20.0"/> + <field name="amount_type">percent</field> + <field name="refund_account_id" ref="pcg_445711"/> + <field name="account_id" ref="pcg_445711"/> + <field name="sequence" eval="10"/> + <field name="type_tax_use">sale</field> + <field name="tag_ids" eval="[(6,0,[ref('l10n_fr.tax_20_0_TTC')])]"/> + </record> + + <record model="account.tax.template" id="tva_specifique_ttc"> + <field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/> + <field name="name">TVA collectée (vente) 8,5% TTC</field> + <field name="description">8.5-TTC</field> + <field name="price_include" eval="1"/> + <field name="amount" eval="8.5"/> + <field name="amount_type">percent</field> + <field name="refund_account_id" ref="pcg_445713"/> + <field name="account_id" ref="pcg_445713"/> + <field name="sequence" eval="10"/> + <field name="type_tax_use">sale</field> + <field name="tag_ids" eval="[(6,0,[ref('l10n_fr.tax_8_5_TTC')])]"/> + </record> + + <record model="account.tax.template" id="tva_intermediaire_ttc"> + <field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/> + <field name="name">TVA collectée (vente) 10,0% TTC</field> + <field name="description">10.0-TTC</field> + <field name="price_include" eval="1"/> + <field name="amount" eval="10.0"/> + <field name="amount_type">percent</field> + <field name="refund_account_id" ref="pcg_445712"/> + <field name="account_id" ref="pcg_445712"/> + <field name="sequence" eval="10"/> + <field name="type_tax_use">sale</field> + <field name="tag_ids" eval="[(6,0,[ref('l10n_fr.tax_10_0_TTC')])]"/> + </record> + + <record model="account.tax.template" id="tva_reduite_ttc"> + <field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/> + <field name="name">TVA collectée (vente) 5,5% TTC</field> + <field name="description">5.5-TTC</field> + <field name="price_include" eval="1"/> + <field name="amount" eval="5.5"/> + <field name="amount_type">percent</field> + <field name="refund_account_id" ref="pcg_445713"/> + <field name="account_id" ref="pcg_445713"/> + <field name="sequence" eval="10"/> + <field name="type_tax_use">sale</field> + <field name="tag_ids" eval="[(6,0,[ref('l10n_fr.tax_5_5_TTC')])]"/> + </record> + + <record model="account.tax.template" id="tva_super_reduite_ttc"> + <field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/> + <field name="name">TVA collectée (vente) 2,1% TTC</field> + <field name="description">2.1-TTC</field> + <field name="price_include" eval="1"/> + <field name="amount" eval="2.1"/> + <field name="amount_type">percent</field> + <field name="refund_account_id" ref="pcg_445713"/> + <field name="account_id" ref="pcg_445713"/> + <field name="sequence" eval="10"/> + <field name="type_tax_use">sale</field> + <field name="tag_ids" eval="[(6,0,[ref('l10n_fr.tax_2_1_TTC')])]"/> + </record> + <!-- ACHATS Tax excluded from price --> <record model="account.tax.template" id="tva_acq_normale"> <field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/> diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index 8508fa9f250..d87cad5fec7 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -488,7 +488,7 @@ class MailTemplate(models.Model): ) # Add report in attachments: generate once for all template_res_ids - if template.report_template and not 'report_template_in_attachment' in self.env.context: + if template.report_template: for res_id in template_res_ids: attachments = [] report_name = self.render_template(template.report_name, template.model, res_id) diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 7363bf6510b..6af6a2206f4 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -1692,12 +1692,14 @@ class MailThread(models.AbstractModel): m2m_attachment_ids += [(4, id) for id in attachment_ids] # Handle attachments parameter, that is a dictionary of attachments for attachment in attachments: + cid = False if len(attachment) == 2: name, content = attachment elif len(attachment) == 3: name, content, info = attachment if info and info.get('cid'): - cid_mapping[info['cid']] = name + cid = info['cid'] + cid_mapping[cid] = name else: continue if isinstance(content, unicode): @@ -1705,7 +1707,7 @@ class MailThread(models.AbstractModel): data_attach = { 'name': name, 'datas': base64.b64encode(str(content)), - 'datas_fname': name, + 'datas_fname': cid or name, 'description': name, 'res_model': message_data['model'], 'res_id': message_data['res_id'], @@ -1718,8 +1720,11 @@ class MailThread(models.AbstractModel): postprocessed = False for node in root.iter('img'): if node.get('src', '').startswith('cid:'): - fname = cid_mapping.get(node.get('src').split('cid:')[1], node.get('data-filename', '')) - attachment = parameter_attachments.filtered(lambda attachment: attachment.name == fname) + cid = node.get('src').split('cid:')[1] + fname = cid_mapping.get(cid, node.get('data-filename', '')) + attachment = parameter_attachments.filtered(lambda attachment: attachment.datas_fname == cid) + if not attachment: + attachment = parameter_attachments.filtered(lambda attachment: attachment.datas_fname == fname) if attachment: node.set('src', '/web/image/%s' % attachment.ids[0]) postprocessed = True diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index acaf1825348..9cf1c22e482 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -192,7 +192,7 @@ class MailComposer(models.TransientModel): @api.multi def send_mail_action(self): # TDE/ ??? - return self.with_context(report_template_in_attachment=True).send_mail() + return self.send_mail() @api.multi def send_mail(self, auto_commit=False): @@ -358,20 +358,6 @@ class MailComposer(models.TransientModel): if template.user_signature and 'body_html' in values: signature = self.env.user.signature values['body_html'] = tools.append_content_to_html(values['body_html'], signature, plaintext=False) - if template.report_template: - attachment = self.env['ir.attachment'] - attach = self.generate_attachment_from_report(template_id, res_id) - for attach_fname, attach_datas in attach[res_id].pop('attachments', []): - data_attach = { - 'name': attach_fname, - 'datas': attach_datas, - 'datas_fname': attach_fname, - 'res_model': 'mail.compose.message', - 'res_id': 0, - 'type': 'binary', - } - values.setdefault('attachment_ids', list()).append(attachment.create(data_attach).id) - elif template_id: values = self.generate_email_for_composer(template_id, [res_id])[res_id] # transform attachments into attachment_ids; not attached to the document because this will @@ -403,13 +389,6 @@ class MailComposer(models.TransientModel): return {'value': values} @api.multi - def generate_attachment_from_report(self, template_id, res_id): - fields = ['attachment_ids'] - result = self.env['mail.template'].with_context(tpl_partners_only=True).browse(template_id).generate_email([res_id], fields=fields) - return result - - - @api.multi def save_as_template(self): """ hit save as template button: current form value will be a new template attached to the current document. """ diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index 0fbc3c030e1..3feb7b498be 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -111,6 +111,11 @@ class MrpBom(models.Model): return self.search(domain, order='sequence, product_id', limit=1) def explode(self, product, quantity, picking_type=False): + """ + Explodes the BoM and creates two lists with all the information you need: bom_done and line_done + Quantity describes the number of times you need the BoM: so the quantity divided by the number created by the BoM + and converted into its UoM + """ boms_done = [(self, {'qty': quantity, 'product': product, 'original_qty': quantity, 'parent_line': False})] lines_done = [] templates_done = product.product_tmpl_id @@ -125,11 +130,11 @@ class MrpBom(models.Model): if current_line.product_id.product_tmpl_id in templates_done: raise UserError(_('Recursion error! A product with a Bill of Material should not have itself in its BoM or child BoMs!')) - line_quantity = current_qty * current_line.product_qty / current_line.bom_id.product_qty + line_quantity = current_qty * current_line.product_qty bom = self._bom_find(product=current_line.product_id, picking_type=picking_type or self.picking_type_id, company_id=self.company_id.id) if bom.type == 'phantom': - converted_line_quantity = current_line.product_uom_id._compute_quantity(line_quantity, bom.product_uom_id) - bom_lines = [(line, current_line.product_id, line_quantity, current_line) for line in bom.bom_line_ids] + bom_lines + converted_line_quantity = current_line.product_uom_id._compute_quantity(line_quantity / bom.product_qty, bom.product_uom_id) + bom_lines = [(line, current_line.product_id, converted_line_quantity, current_line) for line in bom.bom_line_ids] + bom_lines templates_done |= current_line.product_id.product_tmpl_id boms_done.append((bom, {'qty': converted_line_quantity, 'product': current_product, 'original_qty': quantity, 'parent_line': current_line})) else: diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 7d6bc5d881b..d0f5a2f00f4 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -55,7 +55,7 @@ class MrpProduction(models.Model): states={'confirmed': [('readonly', False)]}) product_tmpl_id = fields.Many2one('product.template', 'Product Template', related='product_id.product_tmpl_id') product_qty = fields.Float( - 'Quantity to Produce', + 'Quantity To Produce', default=1.0, digits=dp.get_precision('Product Unit of Measure'), readonly=True, required=True, states={'confirmed': [('readonly', False)]}) @@ -79,12 +79,12 @@ class MrpProduction(models.Model): states={'confirmed': [('readonly', False)]}, help="Location where the system will stock the finished products.") date_planned_start = fields.Datetime( - 'Expected Start Date', copy=False, default=fields.Datetime.now, - index=True, required=True, readonly=True, + 'Deadline Start', copy=False, default=fields.Datetime.now, + index=True, required=True, states={'confirmed': [('readonly', False)]}, oldname="date_planned") date_planned_finished = fields.Datetime( - 'Expected End Date', copy=False, default=fields.Datetime.now, - index=True, readonly=True, + 'Deadline End', copy=False, default=fields.Datetime.now, + index=True, states={'confirmed': [('readonly', False)]}) date_start = fields.Datetime('Start Date', copy=False, index=True, readonly=True) date_finished = fields.Datetime('End Date', copy=False, index=True, readonly=True) @@ -264,7 +264,7 @@ class MrpProduction(models.Model): def _generate_moves(self): for production in self: production._generate_finished_moves() - factor = production.product_uom_id._compute_quantity(production.product_qty, production.bom_id.product_uom_id) + factor = production.product_uom_id._compute_quantity(production.product_qty, 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) production._generate_raw_moves(lines) # Check for all draft moves whether they are mto or not @@ -311,8 +311,7 @@ class MrpProduction(models.Model): source_location = self.bom_id.routing_id.location_id else: source_location = self.location_src_id - original_quantity = self.product_uom_id._compute_quantity(self.product_qty, self.bom_id.product_uom_id) - + original_quantity = self.product_qty - self.qty_produced data = { 'name': self.name, 'date': self.date_planned_start, @@ -354,14 +353,21 @@ class MrpProduction(models.Model): move.procure_method = 'make_to_order' @api.multi - def _update_raw_move(self, bom_line, quantity, **kw): + def _update_raw_move(self, bom_line, line_data): + quantity = line_data['qty'] self.ensure_one() move = self.move_raw_ids.filtered(lambda x: x.bom_line_id.id == bom_line.id and x.state not in ('done', 'cancel')) if move: - move.write({'product_uom_qty': quantity}) + if quantity > 0: + move[0].write({'product_uom_qty': quantity}) + else: + if move[0].quantity_done > 0: + raise UserError(_('Lines need to be deleted, but can not as you still have some quantities to consume in them. ')) + move[0].action_cancel() + move[0].unlink() return move else: - self._generate_raw_move(bom_line, quantity) + self._generate_raw_move(bom_line, line_data) @api.multi def action_assign(self): @@ -381,7 +387,7 @@ class MrpProduction(models.Model): """ Create work orders. And probably do stuff, like things. """ orders_to_plan = self.filtered(lambda order: order.routing_id and order.state == 'confirmed') for order in orders_to_plan: - quantity = order.product_uom_id._compute_quantity(order.product_qty, order.bom_id.product_uom_id) + quantity = order.product_uom_id._compute_quantity(order.product_qty, order.bom_id.product_uom_id) / order.bom_id.product_qty boms, lines = order.bom_id.explode(order.product_id, quantity, picking_type=order.bom_id.picking_type_id) order._generate_workorders(boms) orders_to_plan.write({'state': 'planned'}) @@ -401,19 +407,18 @@ class MrpProduction(models.Model): BoMs """ workorders = self.env['mrp.workorder'] - qty = bom_data['qty'] + bom_qty = bom_data['qty'] - use_serial = any(product.tracking == 'serial' for product in self.mapped('move_finished_ids.product_id')) - if use_serial: + # Initial qty producing + if self.product_id.tracking == 'serial': quantity = 1.0 else: quantity = self.product_qty - sum(self.move_finished_ids.mapped('quantity_done')) quantity = quantity if (quantity > 0) else 0 - # TDE FIXME: what is qty compared to quantity ?? for operation in bom.routing_id.operation_ids: # create workorder - cycle_number = math.ceil(qty / bom.product_qty / operation.workcenter_id.capacity) # TODO: float_round UP + cycle_number = math.ceil(bom_qty / operation.workcenter_id.capacity) # TODO: float_round UP duration_expected = (operation.workcenter_id.time_start + operation.workcenter_id.time_stop + cycle_number * operation.time_cycle * 100.0 / operation.workcenter_id.time_efficiency) @@ -513,7 +518,7 @@ class MrpProduction(models.Model): self.post_inventory() moves_to_cancel = (self.move_raw_ids | self.move_finished_ids).filtered(lambda x: x.state not in ('done', 'cancel')) moves_to_cancel.action_cancel() - self.write({'state': 'done', 'date_finished': fields.datetime.now()}) + self.write({'state': 'done', 'date_finished': fields.Datetime.now()}) self.env["procurement.order"].search([('production_id', 'in', self.ids)]).check() self.write({'state': 'done'}) diff --git a/addons/mrp/models/mrp_routing.py b/addons/mrp/models/mrp_routing.py index b9effeadd8d..832b0328e89 100644 --- a/addons/mrp/models/mrp_routing.py +++ b/addons/mrp/models/mrp_routing.py @@ -69,6 +69,7 @@ class MrpRoutingWorkcenter(models.Model): batch = fields.Selection([ ('no', 'Once all products are processed'), ('yes', 'Once a minimum number of products is processed')], string='Next Operation', + help="""Will determine if the next work order will be planned after the previous one or after the first Quantity To Process of the previous one.""", default='no', required=True) batch_size = fields.Float('Quantity to Process', default=1.0) workorder_ids = fields.One2many('mrp.workorder', 'operation_id', string="Work Orders") diff --git a/addons/mrp/models/mrp_unbuild.py b/addons/mrp/models/mrp_unbuild.py index 0abd787fbf0..7254101505e 100644 --- a/addons/mrp/models/mrp_unbuild.py +++ b/addons/mrp/models/mrp_unbuild.py @@ -154,8 +154,8 @@ class MrpUnbuild(models.Model): def _generate_produce_moves(self): moves = self.env['stock.move'] for unbuild in self: - factor = unbuild.product_uom_id._compute_quantity(unbuild.product_qty, unbuild.bom_id.product_uom_id) - boms, lines = unbuild.bom_id.explode(unbuild.product_id, factor / unbuild.bom_id.product_qty, picking_type=unbuild.bom_id.picking_type_id) + factor = unbuild.product_uom_id._compute_quantity(unbuild.product_qty, unbuild.bom_id.product_uom_id) / unbuild.bom_id.product_qty + boms, lines = unbuild.bom_id.explode(unbuild.product_id, factor, picking_type=unbuild.bom_id.picking_type_id) for line, line_data in lines: moves += unbuild._generate_move_from_bom_line(line, line_data['qty']) return moves diff --git a/addons/mrp/models/mrp_workcenter.py b/addons/mrp/models/mrp_workcenter.py index 161c18e6473..b47294465ce 100644 --- a/addons/mrp/models/mrp_workcenter.py +++ b/addons/mrp/models/mrp_workcenter.py @@ -42,10 +42,10 @@ class MrpWorkcenter(models.Model): ('done', 'In Progress')], 'Status', compute="_compute_working_state", store=True) blocked_time = fields.Float( 'Blocked Time', compute='_compute_blocked_time', - help='Blocked hours over the last month') + help='Blocked hour(s) over the last month', digits=(16, 2)) productive_time = fields.Float( 'Productive Time', compute='_compute_productive_time', - help='Productive hours over the last month') + help='Productive hour(s) over the last month', digits=(16, 2)) oee = fields.Float(compute='_compute_oee', help='Overall Equipment Effectiveness, based on the last month') oee_target = fields.Float(string='OEE Target', help="OEE Target in percentage", default=90) performance = fields.Integer('Performance', compute='_compute_performance', help='Performance over the last month') @@ -99,7 +99,7 @@ class MrpWorkcenter(models.Model): ['duration', 'workcenter_id'], ['workcenter_id'], lazy=False) count_data = dict((item['workcenter_id'][0], item['duration']) for item in data) for workcenter in self: - workcenter.blocked_time = count_data.get(workcenter.id, 0.0) + workcenter.blocked_time = count_data.get(workcenter.id, 0.0) / 60.0 @api.multi def _compute_productive_time(self): @@ -112,15 +112,15 @@ class MrpWorkcenter(models.Model): ['duration', 'workcenter_id'], ['workcenter_id'], lazy=False) count_data = dict((item['workcenter_id'][0], item['duration']) for item in data) for workcenter in self: - workcenter.productive_time = count_data.get(workcenter.id, 0.0) + workcenter.productive_time = count_data.get(workcenter.id, 0.0) / 60.0 @api.depends('blocked_time', 'productive_time') - @api.one def _compute_oee(self): - if self.productive_time: - self.oee = round(self.productive_time * 100.0 / (self.productive_time + self.blocked_time), 2) - else: - self.oee = 0.0 + for order in self: + if order.productive_time: + order.oee = round(order.productive_time * 100.0 / (order.productive_time + order.blocked_time), 2) + else: + order.oee = 0.0 @api.multi def _compute_performance(self): @@ -185,7 +185,7 @@ class MrpWorkcenterProductivity(models.Model): loss_type = fields.Selection( "Effectiveness", related='loss_id.loss_type', store=True) description = fields.Text('Description') - date_start = fields.Datetime('Start Date', default=fields.Datetime.now(), required=True) + date_start = fields.Datetime('Start Date', default=fields.Datetime.now, required=True) date_end = fields.Datetime('End Date') duration = fields.Float('Duration', compute='_compute_duration', store=True) diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index 61a4ccbf21e..920d8b0bc4c 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -214,11 +214,15 @@ class MrpWorkorder(models.Model): for move_lot in move_lots: if qty_todo <= 0: break - if move_lot.quantity_done == 0 and qty_todo > move_lot.quantity: + if not move_lot.lot_id and qty_todo >= move_lot.quantity: qty_todo = qty_todo - move_lot.quantity self.active_move_lot_ids -= move_lot # Difference operator else: move_lot.quantity = move_lot.quantity - qty_todo + if move_lot.quantity_done - qty_todo > 0: + move_lot.quantity_done = move_lot.quantity_done - qty_todo + else: + move_lot.quantity_done = 0 qty_todo = 0 @api.multi diff --git a/addons/mrp/models/product.py b/addons/mrp/models/product.py index b4e46fda938..c3f457adb56 100644 --- a/addons/mrp/models/product.py +++ b/addons/mrp/models/product.py @@ -29,11 +29,8 @@ class ProductTemplate(models.Model): def action_view_mos(self): product_ids = self.mapped('product_variant_ids').ids action = self.env.ref('mrp.act_product_mrp_production').read()[0] - if len(self) == 1 and len(product_ids) == 1: - action['context'] = {'default_product_id': product_ids[0], 'search_default_product_id': product_ids[0]} - else: - action['domain'] = [('product_id', 'in', product_ids)] - action['context'] = {} + action['domain'] = [('product_id', 'in', product_ids)] + action['context'] = {} return action diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index e3dd9e07994..5c539195444 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -165,31 +165,70 @@ class StockMove(models.Model): return True @api.multi + def _create_extra_move(self): + ''' Creates an extra move if necessary depending on extra quantities than foreseen or extra moves''' + self.ensure_one() + quantity_to_split = 0 + extra_move = self.env['stock.move'] + rounding = self.product_uom.rounding + link_procurement = False + if self.procurement_id and self.production_id and float_compare(self.production_id.qty_produced, self.procurement_id.product_qty, precision_rounding=rounding) > 0: + done_moves_total = sum(self.production_id.move_finished_ids.filtered(lambda x: x.product_id == self.product_id and x.state == 'done').mapped('product_uom_qty')) + if float_compare(done_moves_total, self.procurement_id.product_qty, precision_rounding=rounding) >= 0: + quantity_to_split = 0 + self.product_uom_qty = self.quantity_done #TODO: could change qty on move_dest_id also (in case of 2-step in/out) + else: + quantity_to_split = done_moves_total + self.quantity_done - self.procurement_id.product_qty + elif float_compare(self.quantity_done, self.product_uom_qty, precision_rounding=rounding) > 0: + quantity_to_split = self.quantity_done - self.product_uom_qty + link_procurement = True + if quantity_to_split: + extra_move = self.copy(default={'quantity_done': quantity_to_split, 'product_uom_qty': quantity_to_split, 'production_id': self.production_id.id, + 'raw_material_production_id': self.raw_material_production_id.id, + 'procurement_id': link_procurement and self.procurement_id.id or False}) + extra_move.action_confirm() + if self.has_tracking != 'none': + qty_todo = self.quantity_done - quantity_to_split + for movelot in self.move_lot_ids.filtered(lambda x: x.done_wo): + if movelot.quantity_done and movelot.done_wo: + if float_compare(qty_todo, movelot.quantity_done, precision_rounding=rounding) >= 0: + qty_todo -= movelot.quantity_done + elif float_compare(qty_todo, 0, precision_rounding=rounding) > 0: + #split + remaining = movelot.quantity_done - qty_todo + movelot.quantity_done = qty_todo + movelot.copy(default={'move_id': extra_move.id, 'quantity_done': remaining}) + qty_todo = 0 + else: + movelot.move_id = extra_move.id + else: + self.quantity_done = self.product_uom_qty + self.product_uom_qty = self.quantity_done - quantity_to_split + return extra_move + + @api.multi def move_validate(self): ''' Validate moves based on a production order. ''' moves = self._filter_closed_moves() quant_obj = self.env['stock.quant'] moves_todo = self.env['stock.move'] moves_to_unreserve = self.env['stock.move'] + # Create extra moves where necessary for move in moves: rounding = move.product_uom.rounding if float_compare(move.quantity_done, 0.0, precision_rounding=rounding) <= 0: continue moves_todo |= move - if float_compare(move.quantity_done, move.product_uom_qty, precision_rounding=rounding) > 0: - remaining_qty = move.quantity_done - move.product_uom_qty # In UoM of move - extra_move = move.copy(default={'quantity_done': remaining_qty, 'product_uom_qty': remaining_qty, 'production_id': move.production_id.id, - 'raw_material_production_id': move.raw_material_production_id.id}) - move.quantity_done = move.product_uom_qty - extra_move.action_confirm() - moves_todo |= extra_move + moves_todo |= move._create_extra_move() + # Split moves where necessary and move quants for move in moves_todo: - if float_compare(move.quantity_done, move.product_uom_qty, precision_rounding=rounding): + rounding = move.product_uom.rounding + if float_compare(move.quantity_done, move.product_uom_qty, precision_rounding=rounding) < 0: # Need to do some kind of conversion here qty_split = move.product_uom._compute_quantity(move.product_uom_qty - move.quantity_done, move.product_id.uom_id) new_move = move.split(qty_split) # If you were already putting stock.move.lots on the next one in the work order, transfer those to the new move - move.move_lot_ids.filtered(lambda x: not x.done_wo).write({'move_id': new_move}) + move.move_lot_ids.filtered(lambda x: not x.done_wo or x.quantity_done == 0.0).write({'move_id': new_move}) self.browse(new_move).quantity_done = 0.0 main_domain = [('qty', '>', 0)] preferred_domain = [('reservation_id', '=', move.id)] diff --git a/addons/mrp/models/stock_scrap.py b/addons/mrp/models/stock_scrap.py index b78b896c8f0..b672176636b 100644 --- a/addons/mrp/models/stock_scrap.py +++ b/addons/mrp/models/stock_scrap.py @@ -41,6 +41,7 @@ class StockScrap(models.Model): def _prepare_move_values(self): vals = super(StockScrap, self)._prepare_move_values() if self.production_id: + vals['origin'] = vals['origin'] or self.production_id.name if self.product_id in self.production_id.move_finished_ids.mapped('product_id'): vals.update({'production_id': self.production_id.id}) else: diff --git a/addons/mrp/views/mrp_bom_views.xml b/addons/mrp/views/mrp_bom_views.xml index 07b851d168d..45e7d7cc842 100644 --- a/addons/mrp/views/mrp_bom_views.xml +++ b/addons/mrp/views/mrp_bom_views.xml @@ -48,7 +48,7 @@ <field name="product_qty"/> <field name="product_uom_id" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> <field name="attribute_value_ids" widget="many2many_tags" domain="[('product_ids.product_tmpl_id', '=', parent.product_tmpl_id)]" groups="product.group_product_variant"/> - <field name="operation_id" groups="mrp.group_mrp_routings" domain="[('routing_id', '=', parent.routing_id)]"/> + <field name="operation_id" groups="mrp.group_mrp_routings" domain="[('routing_id', '=', parent.routing_id)]" options="{'no_quick_create':True,'no_create_edit':True}"/> </tree> </field> </page> @@ -78,6 +78,7 @@ <field name="arch" type="xml"> <tree string="Bill of Materials" default_order="product_tmpl_id"> <field name="active" invisible="1"/> + <field name="sequence" invisible="1"/> <field name="product_tmpl_id"/> <field name="code"/> <field name="product_id" groups="product.group_product_variant"/> diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml index ed1284cf2c8..003e703818a 100644 --- a/addons/mrp/views/mrp_production_views.xml +++ b/addons/mrp/views/mrp_production_views.xml @@ -48,8 +48,8 @@ <button name="button_mark_done" attrs="{'invisible': [('check_to_done', '=', False)]}" string="Mark as Done" type="object" class="oe_highlight"/> <button name="action_assign" attrs="{'invisible': [('availability', 'in', ('assigned', 'none'))]}" string="Check availability" type="object" class="oe_highlight"/> <button name="button_plan" attrs="{'invisible': ['|', ('state', '!=', 'confirmed'), ('routing_id', '=', False)]}" type="object" string="Create Workorders" class="oe_highlight"/> - <button name="open_produce_product" attrs="{'invisible': ['|', '|', ('availability', 'not in', ['partially_available', 'assigned']), ('state', 'not in', ('confirmed','progress')), ('routing_id', '!=', False)]}" string="Produce" type="object" class="oe_highlight"/> - <button name="open_produce_product" attrs="{'invisible': ['|', '|', ('availability', '!=', 'waiting'), ('state', 'not in', ('confirmed','progress')), ('routing_id', '!=', False)]}" string="Produce" type="object"/> + <button name="open_produce_product" attrs="{'invisible': ['|', '|', '|', ('check_to_done', '=', True), ('availability', 'not in', ['partially_available', 'assigned']), ('state', 'not in', ('confirmed','progress')), ('routing_id', '!=', False)]}" string="Produce" type="object" class="oe_highlight"/> + <button name="open_produce_product" attrs="{'invisible': ['|', '|', '|', ('check_to_done', '=', True), ('availability', '!=', 'waiting'), ('state', 'not in', ('confirmed','progress')), ('routing_id', '!=', False)]}" string="Produce" type="object"/> <button name="post_inventory" string="Post Inventory" type="object" class="oe_highlight" attrs="{'invisible': [('post_visible', '=', False)]}"/> <button name="action_cancel" type="object" string="Cancel" attrs="{'invisible': ['|', ('state', 'in', ('done','cancel')), ('check_to_done', '=', True)]}"/> <button name="button_scrap" type="object" string="Scrap" attrs="{'invisible': ['|', ('availability', '=', 'none'), ('state', 'in', ('cancel'))]}"/> @@ -66,7 +66,7 @@ <span class="o_stat_text">Work Orders</span> </div> </button> - <button name="%(action_mrp_production_moves)d" type="action" string="Inventory Moves" class="oe_stat_button" icon="fa-arrows-v" attrs="{'invisible': [('state', 'not in', ('progress', 'done'))]}"/> + <button name="%(action_mrp_production_moves)d" type="action" string="Inventory Moves" class="oe_stat_button" icon="fa-arrows-v" attrs="{'invisible': [('state', 'not in', ('progress', 'cancel', 'done'))]}"/> <button class="oe_stat_button" name="action_see_move_scrap" type="object" icon="fa-arrows-v" attrs="{'invisible': [('scrap_count', '=', 0)]}"> <div class="o_form_field o_stat_info"> <span class="o_stat_value"><field name="scrap_count"/></span> @@ -103,14 +103,10 @@ <field name="routing_id" groups="mrp.group_mrp_routings"/> </group> <group> - <label for="date_planned_start_wo" string="Expected Date"/> - <div name="date_planned" class="o_row"> - <field name="date_planned_start" attrs="{'readonly': [('move_raw_ids', '!=', False)]}"/> - <label string="to" attrs="{'invisible': [('date_planned_finished', '=', False)]}"/> - <field name="date_planned_finished" attrs="{'readonly': [('move_raw_ids', '!=', False)]}"/> - </div> - <field name="user_id" attrs="{'readonly': [('has_moves', '=', True)]}"/> - <field name="origin" attrs="{'readonly': [('has_moves', '=', True)]}"/> + <field name="date_planned_start"/> + <field name="date_planned_finished" invisible="1"/> + <field name="user_id"/> + <field name="origin"/> <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}" attrs="{'readonly': [('has_moves', '=', True)]}"/> </group> </group> diff --git a/addons/mrp/views/mrp_workcenter_views.xml b/addons/mrp/views/mrp_workcenter_views.xml index a73a03fbdce..e990d94d21c 100644 --- a/addons/mrp/views/mrp_workcenter_views.xml +++ b/addons/mrp/views/mrp_workcenter_views.xml @@ -517,7 +517,7 @@ <field name="workcenter_id"/> <field name="user_id"/> <field name="loss_id"/> - <field name="duration" string="Duration (hours)" sum="Duration"/> + <field name="duration" string="Duration (minutes)" sum="Duration"/> </tree> </field> </record> diff --git a/addons/mrp/views/mrp_workorder_views.xml b/addons/mrp/views/mrp_workorder_views.xml index 99b6d974baa..437a9608160 100644 --- a/addons/mrp/views/mrp_workorder_views.xml +++ b/addons/mrp/views/mrp_workorder_views.xml @@ -100,7 +100,7 @@ <button name="button_finish" type="object" string="Finish Order" attrs="{'invisible': ['|', ('state', '!=', 'progress'), ('is_produced', '=', False)]}" class="btn-info"/> <button name="button_start" type="object" string="Start Working" attrs="{'invisible': ['|', ('working_state', '=', 'blocked'), ('state', '!=', 'pending')]}"/> <button name="button_start" type="object" string="Start Working" attrs="{'invisible': ['|', ('working_state', '=', 'blocked'), ('state', '!=', 'ready')]}" class="btn-success"/> - <button name="record_production" type="object" string="Done" class="btn-success" attrs="{'invisible': ['|', '|', ('working_state', '=', 'blocked'), ('state', '!=', 'progress'), ('is_user_working', '=', False)]}"/> + <button name="record_production" type="object" string="Done" class="btn-success" attrs="{'invisible': ['|', '|', '|', ('is_produced', '=', True), ('working_state', '=', 'blocked'), ('state', '!=', 'progress'), ('is_user_working', '=', False)]}"/> <button name="button_pending" type="object" string="Pause" class="btn-warning" attrs="{'invisible': ['|', '|', ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'pending', 'ready', 'cancel')), ('is_user_working', '=', False)]}"/> <button name="%(act_mrp_block_workcenter_wo)d" type="action" context="{'default_workcenter_id': workcenter_id}" string="Block" class="btn-danger" attrs="{'invisible': ['|', '|', ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'pending', 'ready', 'cancel')), ('is_user_working', '=', False)]}"/> <button name="button_unblock" type="object" string="Unblock" class="btn-danger" attrs="{'invisible': [('working_state', '!=', 'blocked')]}"/> @@ -157,6 +157,7 @@ <field name="quantity" readonly="1"/> <field name="lot_id" context="{'default_product_id': product_id}"/> <field name="quantity_done"/> + <field name="move_id" invisible="1"/> <field name="done_wo" invisible="1"/> </tree> </field> diff --git a/addons/mrp/wizard/change_production_qty.py b/addons/mrp/wizard/change_production_qty.py index 254a13813f0..de98b052a81 100644 --- a/addons/mrp/wizard/change_production_qty.py +++ b/addons/mrp/wizard/change_production_qty.py @@ -13,7 +13,7 @@ class ChangeProductionQty(models.TransientModel): # TDE FIXME: add production_id field mo_id = fields.Many2one('mrp.production', 'Manufacturing Order', required=True) product_qty = fields.Float( - 'Product Qty', + 'Quantity To Produce', digits=dp.get_precision('Product Unit of Measure'), required=True) @api.model @@ -37,32 +37,41 @@ class ChangeProductionQty(models.TransientModel): @api.multi def change_prod_qty(self): - MrpBom = self.env['mrp.bom'] for wizard in self: production = wizard.mo_id produced = sum(production.move_finished_ids.mapped('quantity_done')) if wizard.product_qty < produced: - raise UserError(_("You have already produced %d qty , Please give update quantity more then %d ")%(produced, produced)) + raise UserError(_("You have already processed %d. Please input a quantity higher than %d ")%(produced, produced)) production.write({'product_qty': wizard.product_qty}) - #production.action_compute() - #TODO: Do we still need to change the quantity of a production order? - production_move = production.move_finished_ids.filtered(lambda x : x.state not in ('done', 'cancel') and production.product_id.id == x.product_id.id) - for move in production.move_raw_ids: - bom_point = production.bom_id - # TDE FIXME: this is not the place to do that kind of computation, please - if not bom_point: - bom_point = MrpBom._bom_find(product=production.product_id, picking_type=production.picking_type_id) - if not bom_point: - raise UserError(_("Cannot find bill of material for this production.")) - production.write({'bom_id': bom_point.id}) - if not bom_point: - raise UserError(_("Cannot find bill of material for this production.")) - factor = (production.product_qty - production.qty_produced) * production.product_uom_id.factor / bom_point.product_uom_id.factor - boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id) - for line, line_data in lines: - production._update_raw_move(line, line_data['qty']) + bom_point = production.bom_id + factor = self.env['product.uom']._compute_qty(production.product_uom_id.id, production.product_qty - production.qty_produced, production.bom_id.product_uom_id.id) / production.bom_id.product_qty + boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id) + for line, line_data in lines: + production._update_raw_move(line, line_data) self._update_product_to_produce(production, production.product_qty - production.qty_produced) moves = production.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) moves.do_unreserve() moves.action_assign() + for wo in production.workorder_ids: + if production.product_id.tracking == 'serial': + quantity = 1.0 + else: + quantity = wo.qty_production - wo.qty_produced + quantity = quantity if (quantity > 0) else 0 + wo.qty_producing = quantity + if wo.qty_produced < wo.qty_production and wo.state == 'done': + wo.state = 'progress' + # assign moves; last operation receive all unassigned moves + operation = wo.operation_id + + # TODO: following could be put in a function as it is similar as code in _workorders_create + # TODO: only needed when creating new moves + moves_raw = production.move_raw_ids.filtered(lambda move: move.operation_id == operation and move.state not in ('done', 'cancel')) + if wo == production.workorder_ids[-1]: + moves_raw |= production.move_raw_ids.filtered(lambda move: not move.operation_id) + moves_finished = production.move_finished_ids.filtered(lambda move: move.operation_id == operation) #TODO: code does nothing, unless maybe by_products? + moves_raw.mapped('move_lot_ids').write({'workorder_id': wo.id}) + (moves_finished + moves_raw).write({'workorder_id': wo.id}) + if wo.move_raw_ids.filtered(lambda x: x.product_id.tracking != 'none') and not wo.active_move_lot_ids: + wo._generate_lot_ids() return {} diff --git a/addons/mrp/wizard/change_production_qty_views.xml b/addons/mrp/wizard/change_production_qty_views.xml index 205ecb5010f..0ba2fc8488e 100644 --- a/addons/mrp/wizard/change_production_qty_views.xml +++ b/addons/mrp/wizard/change_production_qty_views.xml @@ -4,7 +4,7 @@ <!-- Change Product Quantity --> <record id="view_change_production_qty_wizard" model="ir.ui.view"> - <field name="name">Change Product Qty</field> + <field name="name">Change Quantity To Produce</field> <field name="model">change.production.qty</field> <field name="arch" type="xml"> <form string="Change Product Qty"> @@ -21,7 +21,7 @@ </record> <record id="action_change_production_qty" model="ir.actions.act_window"> - <field name="name">Change Product Qty</field> + <field name="name">Change Quantity To Produce</field> <field name="type">ir.actions.act_window</field> <field name="res_model">change.production.qty</field> <field name="view_type">form</field> diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py index 2571c83b848..428cb627404 100644 --- a/addons/mrp/wizard/mrp_product_produce.py +++ b/addons/mrp/wizard/mrp_product_produce.py @@ -74,6 +74,8 @@ class MrpProductProduce(models.TransientModel): # Nothing to do for lots since values are created using default data (stock.move.lots) moves = self.production_id.move_raw_ids quantity = self.product_qty + if float_compare(quantity, 0, precision_rounding=self.product_uom_id.rounding) <= 0: + raise UserError(_('You should at least produce some quantity')) for move in moves.filtered(lambda x: x.product_id.tracking == 'none' and x.state not in ('done', 'cancel')): if move.unit_factor: move.quantity_done_store += quantity * move.unit_factor @@ -84,6 +86,8 @@ class MrpProductProduce(models.TransientModel): elif move.unit_factor: move.quantity_done_store += quantity * move.unit_factor self.check_finished_move_lots() + if self.production_id.state == 'confirmed': + self.production_id.state = 'progress' return {'type': 'ir.actions.act_window_close'} @api.multi diff --git a/addons/mrp/wizard/mrp_product_produce_views.xml b/addons/mrp/wizard/mrp_product_produce_views.xml index b129c8be74c..9a22d279766 100644 --- a/addons/mrp/wizard/mrp_product_produce_views.xml +++ b/addons/mrp/wizard/mrp_product_produce_views.xml @@ -10,7 +10,7 @@ <group> <field name="serial" invisible="1"/> <field name="production_id" invisible="1"/> - <field name="product_id"/> + <field name="product_id" readonly="1"/> <label for="product_qty"/> <div class="o_row"> <field name="product_qty" attrs="{'readonly': [('serial', '=', True)]}"/> @@ -27,7 +27,7 @@ <field name="quantity" readonly="1"/> <field name="plus_visible" invisible="1"/> <field name="quantity_done"/> - <field name="lot_id"/> + <field name="lot_id" context="{'default_product_id': product_id}"/> <field name="move_id" invisible="1"/> <button name="do_minus" type="object" icon="fa-minus-square" attrs="{'invisible': [('quantity_done', '<=', 0.99)]}" invisible="context.get('state_done')"/> <button name="do_plus" type="object" icon="fa-plus-square" attrs="{'invisible': [('plus_visible', '=', False)]}" invisible="context.get('state_done')"/> diff --git a/addons/mrp_byproduct/models/mrp_production.py b/addons/mrp_byproduct/models/mrp_production.py index 142775818af..924c13fc2e8 100644 --- a/addons/mrp_byproduct/models/mrp_production.py +++ b/addons/mrp_byproduct/models/mrp_production.py @@ -4,38 +4,40 @@ from odoo import api, models -class Production(models.Model): +class MrpProduction(models.Model): _description = 'Production' _inherit = 'mrp.production' + def _create_byproduct_move(self, sub_product): + Move = self.env['stock.move'] + for production in self: + source = production.product_id.property_stock_production.id + product_uom_factor = production.product_uom_id._compute_quantity(production.product_qty - production.qty_produced, production.bom_id.product_uom_id) + qty1 = sub_product.product_qty + qty1 *= product_uom_factor / production.bom_id.product_qty + data = { + 'name': 'PROD:%s' % production.name, + 'date': production.date_planned_start, + 'product_id': sub_product.product_id.id, + 'product_uom_qty': qty1, + 'product_uom': sub_product.product_uom_id.id, + 'location_id': source, + 'location_dest_id': production.location_dest_id.id, + 'production_id': production.id, + 'origin': production.name, + 'unit_factor': qty1 / (production.product_qty - production.qty_produced), + 'subproduct_id': sub_product.id + } + move = Move.create(data) + move.action_confirm() @api.multi def _generate_moves(self): """ Generates moves and work orders @return: Newly generated picking Id. """ - # TDE FIXME: was action_confirm ?? -> refactored - Move = self.env['stock.move'] - res = super(Production, self)._generate_moves() + res = super(MrpProduction, self)._generate_moves() for production in self.filtered(lambda production: production.bom_id): - source = production.product_id.property_stock_production.id for sub_product in production.bom_id.sub_products: - product_uom_factor = production.product_uom_id._compute_quantity(production.product_qty, production.bom_id.product_uom_id) - qty1 = sub_product.product_qty - qty1 *= product_uom_factor / production.bom_id.product_qty - data = { - 'name': 'PROD:%s' % production.name, - 'date': production.date_planned_start, - 'product_id': sub_product.product_id.id, - 'product_uom_qty': qty1, - 'product_uom': sub_product.product_uom_id.id, - 'location_id': source, - 'location_dest_id': production.location_dest_id.id, - 'production_id': production.id, - 'origin': production.name, - 'unit_factor': qty1 / production.product_qty, - 'subproduct_id': sub_product.id - } - move = Move.create(data) - move.action_confirm() + production._create_byproduct_move(sub_product) return res diff --git a/addons/mrp_byproduct/models/stock_move.py b/addons/mrp_byproduct/models/stock_move.py index e75c2f8351c..119cc8fda94 100644 --- a/addons/mrp_byproduct/models/stock_move.py +++ b/addons/mrp_byproduct/models/stock_move.py @@ -4,7 +4,7 @@ from odoo import fields, models -class Stok_Move(models.Model): +class StockMove(models.Model): _inherit = 'stock.move' subproduct_id = fields.Many2one( diff --git a/addons/mrp_byproduct/wizard/change_production_qty.py b/addons/mrp_byproduct/wizard/change_production_qty.py index ac553ced65c..1ac4b74f867 100644 --- a/addons/mrp_byproduct/wizard/change_production_qty.py +++ b/addons/mrp_byproduct/wizard/change_production_qty.py @@ -4,19 +4,20 @@ from odoo import api, models -class change_production_qty(models.TransientModel): +class ChangeProductionQty(models.TransientModel): _inherit = 'change.production.qty' @api.model def _update_product_to_produce(self, prod, qty): - # U + super(ChangeProductionQty, self)._update_product_to_produce(prod, qty) Production = self.env['mrp.production'] - for move in prod.move_finished_ids: - if move.product_id == prod.product_id: - move.write({'product_uom_qty': qty}) + UoM = self.env['product.uom'] + for sub_product_line in prod.bom_id.sub_products: + move = prod.move_finished_ids.filtered(lambda x: x.sub_product_id == sub_product_line and x.state not in ('done', 'cancel')) + if move: + product_uom_factor = UoM._compute_qty_obj(prod.product_uom_id, prod.product_qty - prod.qty_produced, prod.bom_id.product_uom_id) + qty1 = sub_product_line.product_qty + qty1 *= product_uom_factor / prod.bom_id.product_qty + move[0].write({'product_uom_qty': qty1}) else: - for sub_product_line in prod.bom_id.sub_products: - if sub_product_line.product_id == move.product_id: - factor = Production._get_subproduct_factor(move) - subproduct_qty = qty * factor - move.write({'product_uom_qty': subproduct_qty}) + prod._create_byproduct_move(sub_product_line)
\ No newline at end of file diff --git a/addons/project_issue/models/project_issue.py b/addons/project_issue/models/project_issue.py index 535fe6dd1da..ce310dedb9a 100644 --- a/addons/project_issue/models/project_issue.py +++ b/addons/project_issue/models/project_issue.py @@ -121,8 +121,9 @@ class ProjectIssue(models.Model): @api.onchange('project_id') def _onchange_project_id(self): if self.project_id: - self.partner_id = self.project_id.partner_id.id - self.email_from = self.project_id.partner_id.email + if not self.partner_id and not self.email_from: + self.partner_id = self.project_id.partner_id.id + self.email_from = self.project_id.partner_id.email self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False)]) else: self.partner_id = False diff --git a/addons/purchase_mrp/models/purchase_mrp.py b/addons/purchase_mrp/models/purchase_mrp.py index 2bdf19c2006..4c22ee1a58c 100644 --- a/addons/purchase_mrp/models/purchase_mrp.py +++ b/addons/purchase_mrp/models/purchase_mrp.py @@ -27,7 +27,7 @@ class PurchaseOrderLine(models.Model): bom_delivered = {} if bom: bom_delivered[bom.id] = False - product_uom_qty_bom = self.product_uom._compute_quantity(self.product_qty, bom.product_uom_id) + product_uom_qty_bom = self.product_uom._compute_quantity(self.product_qty, bom.product_uom_id) / bom.product_qty boms, lines = bom.explode(self.product_id, product_uom_qty_bom) for bom_line, data in lines: qty = 0.0 diff --git a/addons/purchase_requisition/views/purchase_requisition_views.xml b/addons/purchase_requisition/views/purchase_requisition_views.xml index 1b7d34aa6e2..d5177adf924 100644 --- a/addons/purchase_requisition/views/purchase_requisition_views.xml +++ b/addons/purchase_requisition/views/purchase_requisition_views.xml @@ -131,7 +131,7 @@ <group> <field name="user_id" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}"/> <field name="type_id" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}" widget="selection"/> - <field name="vendor_id"/> + <field name="vendor_id" context="{'search_default_supplier':1, 'default_supplier':1, 'default_customer':0}" domain="[('supplier','=',True)]"/> <field name="account_analytic_id" colspan="2" groups="purchase.group_analytic_accounting" attrs="{'readonly': [('state','not in',('draft'))]}"/> </group> <group> diff --git a/addons/rating_project/models/project.py b/addons/rating_project/models/project.py index 3a981885686..a82ca8cc1b9 100644 --- a/addons/rating_project/models/project.py +++ b/addons/rating_project/models/project.py @@ -36,7 +36,8 @@ class Task(models.Model): for task in self: rating_template = task.stage_id.rating_template_id if rating_template: - task.rating_send_request(rating_template, reuse_rating=False) + force_send = self.env.context.get('force_send', True) + task.rating_send_request(rating_template, reuse_rating=False, force_send=force_send) def _rating_get_partner_id(self): res = super(Task, self)._rating_get_partner_id() @@ -56,7 +57,7 @@ class Project(models.Model): @api.model def _send_rating_all(self): projects = self.search([('rating_status', '=', 'periodic'), ('rating_request_deadline', '<=', fields.Datetime.now())]) - projects._send_rating_mail() + projects.with_context(force_send=False)._send_rating_mail() projects._compute_rating_request_deadline() def _send_rating_mail(self): diff --git a/addons/sale_mrp/sale_mrp.py b/addons/sale_mrp/sale_mrp.py index 2247c8f7604..fce9f550395 100644 --- a/addons/sale_mrp/sale_mrp.py +++ b/addons/sale_mrp/sale_mrp.py @@ -38,7 +38,7 @@ class SaleOrderLine(models.Model): bom = self.env['mrp.bom']._bom_find(product=self.product_id) if bom and bom.type == 'phantom': bom_delivered[bom.id] = False - product_uom_qty_bom = self.product_uom._compute_quantity(self.product_uom_qty, bom.product_uom_id) + product_uom_qty_bom = self.product_uom._compute_quantity(self.product_uom_qty, bom.product_uom_id) / bom.product_qty boms, lines = bom.explode(self.product_id, product_uom_qty_bom) for bom_line, data in lines: qty = 0.0 diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py index 1cd94fcccab..9aaa21ed16e 100644 --- a/addons/sale_mrp/tests/test_sale_mrp_flow.py +++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py @@ -282,7 +282,7 @@ class TestSaleMrpFlow(common.TransactionCase): mnf_product_d.post_inventory() # Check state of manufacturing order. - self.assertEqual(mnf_product_d.state, 'confirmed', 'Manufacturing order should still be in confirmed state.') + self.assertEqual(mnf_product_d.state, 'progress', 'Manufacturing order should still be in progress state.') # Check available quantity of product D. self.assertEqual(product_d.qty_available, 20, 'Wrong quantity available of product D.') @@ -334,7 +334,7 @@ class TestSaleMrpFlow(common.TransactionCase): mnf_product_a.post_inventory() # Check state of manufacturing order product A. - self.assertEqual(mnf_product_a.state, 'confirmed', 'Manufacturing order should still be in confirmed state.') + self.assertEqual(mnf_product_a.state, 'progress', 'Manufacturing order should still be in the progress state.') # Check product A avaialble quantity should be 120. self.assertEqual(product_a.qty_available, 120, 'Wrong quantity available of product A.') diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py index 2b3192dc0cf..dc4b52bdf14 100644 --- a/addons/stock/models/product.py +++ b/addons/stock/models/product.py @@ -234,8 +234,8 @@ class Product(models.Model): dest_loc_domain = dest_loc_domain + [('location_dest_id', operator, [location.id for location in other_locations])] return ( domain + loc_domain, - domain + ['&'] + dest_loc_domain + ['!'] + loc_domain, - domain + ['&'] + loc_domain + ['!'] + dest_loc_domain + domain + dest_loc_domain + ['!'] + loc_domain if loc_domain else domain + dest_loc_domain, + domain + loc_domain + ['!'] + dest_loc_domain if dest_loc_domain else domain + loc_domain ) def _search_virtual_available(self, operator, value): diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 34d98ba27ef..7cb80c8b360 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -264,11 +264,6 @@ class StockMove(models.Model): raise UserError(_('Quantities, Units of Measure, Products and Locations cannot be modified on stock moves that have already been processed (except by the Administrator).')) propagated_changes_dict = {} - #propagation of quantity change - if vals.get('product_uom_qty'): - propagated_changes_dict['product_uom_qty'] = vals['product_uom_qty'] - if vals.get('product_uom_id'): - propagated_changes_dict['product_uom_id'] = vals['product_uom_id'] #propagation of expected date: propagated_date_field = False if vals.get('date_expected'): @@ -634,6 +629,7 @@ class StockMove(models.Model): if move.state != 'assigned' and not self.env.context.get('reserve_only_ops'): qty_already_assigned = move.reserved_availability qty = move.product_qty - qty_already_assigned + quants = Quant.quants_get_preferred_domain(qty, move, domain=main_domain[move.id], preferred_domain_list=[]) Quant.quants_reserve(quants, move) diff --git a/addons/stock/models/stock_scrap.py b/addons/stock/models/stock_scrap.py index a0465d5e3f8..355f0139d04 100644 --- a/addons/stock/models/stock_scrap.py +++ b/addons/stock/models/stock_scrap.py @@ -93,7 +93,7 @@ class StockScrap(models.Model): self.ensure_one() return { 'name': self.name, - 'origin': self.origin, + 'origin': self.origin or self.picking_id.name, 'product_id': self.product_id.id, 'product_uom': self.product_uom_id.id, 'product_uom_qty': self.scrap_qty, diff --git a/addons/stock/views/stock_pack_operation_views.xml b/addons/stock/views/stock_pack_operation_views.xml index 5f7e46850e6..e982fac90bf 100644 --- a/addons/stock/views/stock_pack_operation_views.xml +++ b/addons/stock/views/stock_pack_operation_views.xml @@ -21,8 +21,8 @@ </div> </group> <group string="Locations" groups="stock.group_stock_multi_locations"> - <field name="location_id"/> - <field name="location_dest_id"/> + <field name="location_id" domain="[('id', 'child_of', picking_source_location_id)]"/> + <field name="location_dest_id" domain="[('id', 'child_of', picking_destination_location_id)]"/> </group> <group string="Packages" groups="stock.group_tracking_lot"> <field name="package_id" attrs="{'required': [('product_id', '=', False)]}" domain="[('location_id', '=', location_id)]" context="{'location_id': location_id}"/> diff --git a/addons/web/static/src/js/views/form_relational_widgets.js b/addons/web/static/src/js/views/form_relational_widgets.js index 4ce7ed6dbf3..dd4c4fb0fb6 100644 --- a/addons/web/static/src/js/views/form_relational_widgets.js +++ b/addons/web/static/src/js/views/form_relational_widgets.js @@ -955,7 +955,7 @@ var X2ManyListView = ListView.extend({ field.no_rerender = true; current_values[field.name] = field.get('value'); }); - var cached_records = _.filter(this.dataset.cache, function(item){return !_.isEmpty(item.values);}); + var cached_records = _.filter(this.dataset.cache, function(item){return !_.isEmpty(item.values) && !item.to_delete;}); var valid = _.every(cached_records, function(record){ _.each(fields, function(field){ var value = record.values[field.name]; diff --git a/addons/web/static/src/less/webclient_layout.less b/addons/web/static/src/less/webclient_layout.less index 3355f4f791f..034f00ad3d9 100644 --- a/addons/web/static/src/less/webclient_layout.less +++ b/addons/web/static/src/less/webclient_layout.less @@ -34,6 +34,7 @@ .o-flex(1, 1, auto); .o-flex-display(); min-height: 0; // Allow flex to shrink this element as much as it wants + height: 100%; .o_sub_menu { .o-flex(0, 0, @odoo-submenu-width); diff --git a/addons/web_editor/static/src/js/snippets.options.js b/addons/web_editor/static/src/js/snippets.options.js index 6d4800cd0db..4d533cadd00 100644 --- a/addons/web_editor/static/src/js/snippets.options.js +++ b/addons/web_editor/static/src/js/snippets.options.js @@ -609,7 +609,7 @@ odoo.define('web_editor.snippets.options', function (require) { var _editor = new widget.MediaDialog(null, {}, null, $image[0]).open(); _editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden'); - _editor.on('saved', this, function () { + _editor.on('save', this, function () { var value = $image.attr("src"); this.background(type, value, $li); this.$target.addClass("oe_custom_bg"); diff --git a/addons/web_editor/static/src/js/widgets.js b/addons/web_editor/static/src/js/widgets.js index 00a693d40f2..7a3ab09dd76 100644 --- a/addons/web_editor/static/src/js/widgets.js +++ b/addons/web_editor/static/src/js/widgets.js @@ -34,11 +34,11 @@ Dialog = Dialog.extend({ self.$('input:first').focus(); }); this.on("closed", this, function () { - this.trigger(this.destroyAction); + this.trigger(this.destroyAction, this.final_data || null); }); }, save: function () { - this.destroyAction = "saved"; + this.destroyAction = "save"; this.close(); }, }); @@ -164,8 +164,8 @@ var MediaDialog = Dialog.extend({ }, save: function () { if (this.options.select_images) { - this.trigger("saved", this.active.save()); - this.close(); + this.final_data = this.active.save(); + this._super.apply(this, arguments); return; } if(this.rte) { @@ -198,8 +198,8 @@ var MediaDialog = Dialog.extend({ } var media = this.active.media; - $(document.body).trigger("media-saved", [media, self.old_media]); - self.trigger("saved", [media, self.old_media]); + this.final_data = [media, self.old_media]; + $(document.body).trigger("media-saved", this.final_data); // Update editor bar after image edition (in case the image change to icon or other) _.defer(function () { @@ -312,10 +312,8 @@ var ImageDialog = Widget.extend({ }, save: function () { if (this.options.select_images) { - this.parent.trigger("save", this.images); return this.images; } - this.parent.trigger("save", this.media); var img = this.images[0]; if (!img) { @@ -668,7 +666,6 @@ var fontIconsDialog = Widget.extend({ */ save: function () { var self = this; - this.parent.trigger("save", this.media); var style = this.media.attributes.style ? this.media.attributes.style.value : ''; var classes = (this.media.className||"").split(/\s+/); var custom_classes = /^fa(-[1-5]x|spin|rotate-(9|18|27)0|flip-(horizont|vertic)al|fw|border)?$/; @@ -684,6 +681,8 @@ var fontIconsDialog = Widget.extend({ style = style.replace(/\s*width:[^;]+/, ''); } $(this.media).attr("class", _.compact(final_classes).join(' ')).attr("style", style); + + return this.media; }, /** * return the data font object (with base, parser and icons) or null @@ -907,7 +906,6 @@ var VideoDialog = Widget.extend({ return false; }, save: function () { - this.parent.trigger("save", this.media); var video_id = this.$("#video_id").val(); if (!video_id) { this.$("button.btn-primary").click(); @@ -921,6 +919,8 @@ var VideoDialog = Widget.extend({ '</div>'); $(this.media).replaceWith($iframe); this.media = $iframe[0]; + + return this.media; }, clear: function () { if (this.media.dataset.src) { @@ -1095,7 +1095,7 @@ var LinkDialog = Dialog.extend({ if (classes.replace(/(^|[ ])(btn-default|btn-success|btn-primary|btn-info|btn-warning|btn-danger)([ ]|$)/gi, ' ')) { self.data.style = {'background-color': '', 'color': ''}; } - self.trigger("save", self.data); + self.final_data = self.data; }).then(_super); }, bind_data: function () { diff --git a/addons/website/static/src/js/website.contentMenu.js b/addons/website/static/src/js/website.contentMenu.js index 82bbefcf8c2..aa695857746 100644 --- a/addons/website/static/src/js/website.contentMenu.js +++ b/addons/website/static/src/js/website.contentMenu.js @@ -186,8 +186,8 @@ var SelectEditMenuDialog = widget.Dialog.extend({ }, options || {})); }, save: function () { - this.trigger("save", parseInt(this.$el.find("select").val() || null)); - this._super(); + this.final_data = parseInt(this.$el.find("select").val() || null); + this._super.apply(this, arguments); } }); diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index 046da64ccf7..c765e335f7c 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -175,9 +175,10 @@ odoo.define('website.website', function (require) { }); /* ----- PUBLISHING STUFF ---- */ - $(document).on('click', '.js_publish_management .js_publish_btn', function () { + $(document).on('click', '.js_publish_management .js_publish_btn', function (e) { + e.preventDefault(); + var $data = $(this).parents(".js_publish_management:first"); - var self=this; ajax.jsonRpc($data.data('controller') || '/website/publish', 'call', {'id': +$data.data('id'), 'object': $data.data('object')}) .then(function (result) { $data.toggleClass("css_unpublished css_published"); diff --git a/addons/website/static/src/js/website.seo.js b/addons/website/static/src/js/website.seo.js index 4b5acc8090a..09e6c00a701 100644 --- a/addons/website/static/src/js/website.seo.js +++ b/addons/website/static/src/js/website.seo.js @@ -343,8 +343,8 @@ var Configurator = Dialog.extend({ template: 'website.seo_configuration', events: { 'keyup input[name=seo_page_keywords]': 'confirmKeyword', - 'keyup input[name=seo_page_title]': 'titleChanged', - 'keyup textarea[name=seo_page_description]': 'descriptionChanged', + 'blur input[name=seo_page_title]': 'titleChanged', + 'blur textarea[name=seo_page_description]': 'descriptionChanged', 'click button[data-action=add]': 'addKeyword', }, canEditTitle: false, diff --git a/addons/website/static/src/js/website.snippets.gallery.js b/addons/website/static/src/js/website.snippets.gallery.js index 481ba06e6f3..cde906ca764 100644 --- a/addons/website/static/src/js/website.snippets.gallery.js +++ b/addons/website/static/src/js/website.snippets.gallery.js @@ -67,7 +67,7 @@ options.registry.gallery = options.Class.extend({ }, bind_change: function () { var self = this; - return this.$target.find("img").off('saved').on('saved', function (event, img) { + return this.$target.find("img").off('save').on('save', function (event, img) { var $parent = $(img).parent(); $parent.addClass("saved_active"); var index = self.$target.find(".item.saved_active").index(); @@ -228,7 +228,7 @@ options.registry.gallery = options.Class.extend({ var $container = this.$target.find(".container:first"); var editor = new widget.MediaDialog(null, {select_images: true}, this.$target.closest('.o_editable'), null).open(); var index = Math.max(0, _.max(_.map(this.$target.find("img").get(), function (img) { return img.dataset.index | 0; })) + 1); - editor.on('saved', this, function (attachments) { + editor.on('save', this, function (attachments) { for (var i = 0 ; i < attachments.length; i++) { $('<img class="img img-responsive mb8 mt8"/>') .attr("src", attachments[i].src) diff --git a/addons/website_blog/static/src/js/website_blog.editor.js b/addons/website_blog/static/src/js/website_blog.editor.js index c49ed85548b..85a2d2d7901 100644 --- a/addons/website_blog/static/src/js/website_blog.editor.js +++ b/addons/website_blog/static/src/js/website_blog.editor.js @@ -110,7 +110,7 @@ odoo.define('website_blog.editor', function (require) { var $image = $("<img/>", {src: this.$image.css("background-image")}); var editor = new widget.MediaDialog(null, {only_images: true}, $image, $image[0]).open(); - editor.on("saved", this, function (event, img) { + editor.on("save", this, function (event, img) { var src = $image.attr("src"); this.$image.css("background-image", src ? ("url(" + src + ")") : ""); if (!this.$target.hasClass("cover")) { diff --git a/addons/website_blog/views/website_blog_templates.xml b/addons/website_blog/views/website_blog_templates.xml index 93c97a9c8ac..f6a4ac3a185 100644 --- a/addons/website_blog/views/website_blog_templates.xml +++ b/addons/website_blog/views/website_blog_templates.xml @@ -124,12 +124,12 @@ <div class="col-md-8 col-md-offset-2" t-ignore="True" id="main_column"> <div t-if="not blog_posts" class="container mb64"> - <p class="css_editable_hidden"> - <h1>No blog post yet.</h1> - </p> + <p class="css_editable_hidden"> + <h1>No blog post yet.</h1> + </p> <p groups="website.group_website_designer"> - Click on "Content" on the top menu to write your first blog post. - </p> + Click on "New Page" in the top-right corner to write your first blog post. + </p> </div> <div t-foreach="blog_posts" t-as="blog_post" class="mb32" name="blog_post"> diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 9052e9a5dc3..a8a25ee3bdb 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -295,6 +295,11 @@ class Website(models.Model): def sale_product_domain(self): return [("sale_ok", "=", True)] + @api.model + def sale_get_payment_term(self, partner): + DEFAULT_PAYMENT_TERM = 'account.account_payment_term_immediate' + return self.env.ref(DEFAULT_PAYMENT_TERM, False).id or partner.property_payment_term_id.id + @api.multi def sale_get_order(self, force_create=False, code=None, update_pricelist=False, force_pricelist=False): """ Return the current sale order after mofications specified by params. @@ -339,7 +344,7 @@ class Website(models.Model): sale_order = self.env['sale.order'].sudo().create({ 'partner_id': partner.id, 'pricelist_id': pricelist_id, - 'payment_term_id': partner.property_payment_term_id.id, + 'payment_term_id': self.sale_get_payment_term(partner), 'team_id': self.salesteam_id.id, 'partner_invoice_id': addr['invoice'], 'partner_shipping_id': addr['delivery'], @@ -380,6 +385,7 @@ class Website(models.Model): sale_order.write({'partner_id': partner.id}) sale_order.onchange_partner_id() sale_order.onchange_partner_shipping_id() # fiscal position + sale_order['payment_term_id'] = self.sale_get_payment_term(partner) # check the pricelist : update it if the pricelist is not the 'forced' one values = {} diff --git a/addons/website_sale/static/src/css/website_sale.css b/addons/website_sale/static/src/css/website_sale.css index d5c3d999e47..f1def903329 100644 --- a/addons/website_sale/static/src/css/website_sale.css +++ b/addons/website_sale/static/src/css/website_sale.css @@ -366,6 +366,7 @@ div#payment_method .list-group-item { min-height: 100px; position: relative; padding-bottom: 5px; + padding-left: 180px; } .oe_product.oe_list .oe_product_image { top: 0; @@ -377,9 +378,10 @@ div#payment_method .list-group-item { .oe_product.oe_list section { position: relative; border: 0; - top: 0; + top: auto; bottom: auto; - left: 180px; + left: auto; + right: auto; background: transparent; } } diff --git a/addons/website_sale/static/src/css/website_sale.sass b/addons/website_sale/static/src/css/website_sale.sass index e6dc2a037ad..7cc6cfc0341 100644 --- a/addons/website_sale/static/src/css/website_sale.sass +++ b/addons/website_sale/static/src/css/website_sale.sass @@ -314,6 +314,7 @@ div#payment_method min-height: 100px position: relative padding-bottom: 5px + padding-left: 180px .oe_product_image top: 0 bottom: 0 @@ -323,9 +324,10 @@ div#payment_method section position: relative border: 0 - top: 0 + top: auto bottom: auto - left: 180px + left: auto + right: auto background: transparent .oe_website_sale .row .row .col-md-12 diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index ce62fbf6af4..be19877fb82 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -1139,7 +1139,8 @@ <t t-if="mode == ('new', 'billing')"> <h3 class="page-header mt16">Your Address <small> or </small> - <a href='/web/signup?redirect=/shop/checkout' class='btn btn-primary' style="margin-top: -11px">Sign Up</a> + <t t-set='connect' t-value="request.env['ir.config_parameter'].sudo().get_param('auth_signup.allow_uninvited') == 'True' and ('signup', 'Sign Up') or ('login', 'Log In')"/> + <a t-attf-href='/web/{{connect[0]}}?redirect=/shop/checkout' class='btn btn-primary' style="margin-top: -11px"><t t-esc='connect[1]'/></a> </h3> </t> <t t-if="mode == ('edit', 'billing')"> diff --git a/addons/website_slides/static/src/img/channel-default.jpg b/addons/website_slides/static/src/img/channel-default.jpg Binary files differnew file mode 100644 index 00000000000..dce2a23b9b2 --- /dev/null +++ b/addons/website_slides/static/src/img/channel-default.jpg diff --git a/addons/website_slides/view/website_slides.xml b/addons/website_slides/view/website_slides.xml index d5d0826889c..0441b030ed9 100644 --- a/addons/website_slides/view/website_slides.xml +++ b/addons/website_slides/view/website_slides.xml @@ -44,7 +44,7 @@ <div> <a t-attf-href="/slides/#{slug(channel)}"> <img t-if="channel.promoted_slide_id" t-attf-src="/web/image/slide.slide/#{channel.promoted_slide_id.id}/image_medium" class="img-responsive oe_slides_channel_thumbnail"/> - <img t-if="not channel.promoted_slide_id" src="/website_slides/static/description/channel-default.jpg" class="img-responsive oe_slides_opacity"/> + <img t-if="not channel.promoted_slide_id" src="/website_slides/static/src/img/channel-default.jpg" class="img-responsive oe_slides_opacity"/> </a> </div> <div> diff --git a/doc/cla/individual/bfotop.md b/doc/cla/individual/bfotop.md new file mode 100644 index 00000000000..8697a595ec7 --- /dev/null +++ b/doc/cla/individual/bfotop.md @@ -0,0 +1,11 @@ + + +Greece, 2016-09-23 + +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, + +Basilis Fotopoulos bfotop@tax-solutions.gr https://github.com/bfotop diff --git a/doc/cla/individual/kaerdsar.md b/doc/cla/individual/kaerdsar.md new file mode 100644 index 00000000000..70e63803522 --- /dev/null +++ b/doc/cla/individual/kaerdsar.md @@ -0,0 +1,11 @@ +Cuba, 08/10/2016 + +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, + +Cesar Lage kaerdsar@gmail.com https://github.com/kaerdsar diff --git a/doc/cla/individual/mcb30.md b/doc/cla/individual/mcb30.md new file mode 100644 index 00000000000..b3c601a1ff6 --- /dev/null +++ b/doc/cla/individual/mcb30.md @@ -0,0 +1,11 @@ +United Kingdom, 2016-08-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, + +Michael Brown mbrown@fensystems.co.uk https://github.com/mcb30 diff --git a/doc/setup/install.rst b/doc/setup/install.rst index 0f1de4f14c0..3e5940d8ba1 100644 --- a/doc/setup/install.rst +++ b/doc/setup/install.rst @@ -61,11 +61,11 @@ refer to :ref:`setup/enterprise` (except for :ref:`setup/install/source`). If you wish to access the Enterprise installers/source code, you can: * Go to the Download_ page and log in with your customer credentials -* Download the source on GitHub using git_ +* Download the source on GitHub using git_(available to partners only) .. note:: If you do not have access to our Enterprise repository, you can request it be e-mailing your sales representative or our online support with - your subscription number and GitHub username. + your subscription number and GitHub username. (available to partners only) .. warning:: Enterprise deb and rpm packages do not have repositories, so automatic update will not work. Reinstalling the latest package version will be needed diff --git a/odoo/addons/base/ir/ir_config_parameter.py b/odoo/addons/base/ir/ir_config_parameter.py index 5634277c06b..744ac82db50 100644 --- a/odoo/addons/base/ir/ir_config_parameter.py +++ b/odoo/addons/base/ir/ir_config_parameter.py @@ -5,9 +5,12 @@ Store database-specific configuration parameters """ import uuid +import logging from odoo import api, fields, models -from odoo.tools import config, ormcache +from odoo.tools import config, ormcache, mute_logger + +_logger = logging.getLogger(__name__) """ A dictionary holding some configuration parameters to be initialized when the database is created. @@ -34,6 +37,7 @@ class IrConfigParameter(models.Model): ] @api.model_cr + @mute_logger('openerp.addons.base.ir.ir_config_parameter') def init(self, force=False): """ Initializes the parameters listed in _default_parameters. @@ -82,6 +86,8 @@ class IrConfigParameter(models.Model): group = self.env.ref(group_xml, raise_if_not_found=False) if group: gids.append((4, group.id)) + else: + _logger.warning('Potential Security Issue: Group [%s] is not found.' % group_xml) vals = {'value': value} if gids: diff --git a/odoo/tools/translate.py b/odoo/tools/translate.py index 845b23c44e1..ebc3dd616f7 100644 --- a/odoo/tools/translate.py +++ b/odoo/tools/translate.py @@ -305,12 +305,16 @@ def html_translate(callback, value): if not value: return value - parser = etree.HTMLParser(encoding='utf-8') - trans = XMLTranslator(callback, 'html', parser) - wrapped = "<div>%s</div>" % encode(value) - root = etree.fromstring(wrapped, parser) - trans.process(root[0][0]) # html > body > div - return trans.get_done()[5:-6] # remove tags <div> and </div> + try: + parser = etree.HTMLParser(encoding='utf-8') + trans = XMLTranslator(callback, 'html', parser) + wrapped = "<div>%s</div>" % encode(value) + root = etree.fromstring(wrapped, parser) + trans.process(root[0][0]) # html > body > div + value = trans.get_done()[5:-6] # remove tags <div> and </div> + except ValueError: + _logger.exception("Cannot translate malformed HTML, using source value instead") + return value # |
