diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-05-02 13:16:27 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-05-02 13:16:27 +0200 |
| commit | 3ca58705c98f5976efecfdcd20ae603dc9ecbbe1 (patch) | |
| tree | b4503ca7b2612ff8dafa761dc824452ce1de020a /addons | |
| parent | 848a1b970bd493363b29883c0c20c55c820d04ee (diff) | |
| parent | 2c68df0ab43cb42e6762d7a1929471bf3f3df920 (diff) | |
[MERGE] forward port branch saas-11.3 up to 2c68df0ab43
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/account/models/account_move.py | 2 | ||||
| -rw-r--r-- | addons/account/models/account_payment.py | 14 | ||||
| -rw-r--r-- | addons/account/views/report_payment_receipt_templates.xml | 2 | ||||
| -rw-r--r-- | addons/l10n_in_hr_payroll/views/l10n_in_hr_payroll_view.xml | 4 | ||||
| -rw-r--r-- | addons/point_of_sale/models/pos_order.py | 2 | ||||
| -rw-r--r-- | addons/point_of_sale/static/src/js/screens.js | 122 | ||||
| -rw-r--r-- | addons/point_of_sale/static/src/xml/pos.xml | 3 | ||||
| -rw-r--r-- | addons/product/models/product_template.py | 3 | ||||
| -rw-r--r-- | addons/purchase_stock/models/account_invoice.py | 6 | ||||
| -rw-r--r-- | addons/purchase_stock/tests/test_stockvaluation.py | 241 | ||||
| -rw-r--r-- | addons/stock/models/stock_picking.py | 9 | ||||
| -rw-r--r-- | addons/stock/views/stock_move_views.xml | 32 | ||||
| -rw-r--r-- | addons/stock/views/stock_picking_views.xml | 13 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/calendar/calendar_model.js | 3 | ||||
| -rw-r--r-- | addons/web/static/tests/views/calendar_tests.js | 61 | ||||
| -rw-r--r-- | addons/website_forum/controllers/main.py | 8 | ||||
| -rw-r--r-- | addons/website_hr_recruitment/models/hr_recruitment.py | 2 |
17 files changed, 470 insertions, 57 deletions
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 3ebb1702d0e..6d11d14aa8b 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -994,7 +994,7 @@ class AccountMoveLine(models.Model): partner_id = self.env['res.partner']._find_accounting_partner(self[0].partner_id).id company_currency = self[0].account_id.company_id.currency_id - writeoff_currency = self[0].currency_id or company_currency + writeoff_currency = self[0].account_id.currency_id or company_currency line_to_reconcile = self.env['account.move.line'] # Iterate and create one writeoff by journal writeoff_moves = self.env['account.move'] diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index 3b0e8800d6c..3ba36b049a6 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -831,3 +831,17 @@ class account_payment(models.Model): }) return vals + + def _get_invoice_payment_amount(self, inv): + """ + Computes the amount covered by the current payment in the given invoice. + + :param inv: an invoice object + :returns: the amount covered by the payment in the invoice + """ + self.ensure_one() + return sum([ + data['amount'] + for data in inv._get_payments_vals() + if data['account_payment_id'] == self.id + ]) diff --git a/addons/account/views/report_payment_receipt_templates.xml b/addons/account/views/report_payment_receipt_templates.xml index 871ae39479f..c0e11a2ef7f 100644 --- a/addons/account/views/report_payment_receipt_templates.xml +++ b/addons/account/views/report_payment_receipt_templates.xml @@ -46,7 +46,7 @@ <td><span t-field="inv.date_invoice"/></td> <td><span t-field="inv.number"/></td> <td class="text-right"><span t-field="inv.amount_total"/></td> - <td class="text-right"><span t-esc="inv.amount_total - inv.residual" t-options="{'widget': 'monetary', 'display_currency': inv.currency_id}"/></td> + <td class="text-right"><span t-esc="o._get_invoice_payment_amount(inv)" t-options="{'widget': 'monetary', 'display_currency': inv.currency_id}"/></td> <td class="text-right"><span t-field="inv.residual"/></td> </tr> </tbody> diff --git a/addons/l10n_in_hr_payroll/views/l10n_in_hr_payroll_view.xml b/addons/l10n_in_hr_payroll/views/l10n_in_hr_payroll_view.xml index 56ba9b00694..175b8e2fe9a 100644 --- a/addons/l10n_in_hr_payroll/views/l10n_in_hr_payroll_view.xml +++ b/addons/l10n_in_hr_payroll/views/l10n_in_hr_payroll_view.xml @@ -3,10 +3,10 @@ <record id="hr_contract_form_in_inherit" model="ir.ui.view"> <field name="name">hr.contract.form.in.inherit</field> <field name="model">hr.contract</field> - <field name="inherit_id" ref="hr_payroll.hr_contract_form_inherit"/> + <field name="inherit_id" ref="hr_contract.hr_contract_view_form"/> <field name="arch" type="xml"> <data> - <xpath expr="//field[@name='struct_id']" position="after"> + <xpath expr="//group[@name='duration_group']" position="after"> <group col="2" colspan="2" name="right_column"> <separator colspan="2" string="Allowance"/> <field name="driver_salay"/> diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 92db22b1217..e9c6107bbdd 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -745,7 +745,7 @@ class PosOrder(models.Model): if to_invoice: pos_order.action_pos_order_invoice() - pos_order.invoice_id.sudo().action_invoice_open() + pos_order.invoice_id.sudo().with_context(force_company=self.env.user.company_id.id).action_invoice_open() pos_order.account_move = pos_order.invoice_id.move_id return order_ids diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index cd149543475..a91391441b3 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -178,6 +178,61 @@ var ScreenWidget = PosBaseWidget.extend({ } } }, + /** + * Handles the error response from the server when we push + * an invoiceable order + * Displays appropriates warnings and errors and + * proposes subsequent actions + * + * @private + * @param {PosModel} order: the order to consider, defaults to current order + * @param {Boolean} refresh_screens: whether or not displayed screens should refresh + * @param {Object} error: the error provided by Ajax + */ + _handleFailedPushForInvoice: function (order, refresh_screen, error) { + var self = this; + order = order || this.pos.get_order(); + this.invoicing = false; + order.finalized = false; + if (error.message === 'Missing Customer') { + this.gui.show_popup('confirm',{ + 'title': _t('Please select the Customer'), + 'body': _t('You need to select the customer before you can invoice an order.'), + confirm: function(){ + self.gui.show_screen('clientlist', null, refresh_screen); + }, + }); + } else if (error.message === 'Backend Invoice') { + this.gui.show_popup('confirm',{ + 'title': _t('Please print the invoice from the backend'), + 'body': _t('The order has been synchronized earlier. Please make the invoice from the backend for the order: ') + error.data.order.name, + confirm: function () { + this.gui.show_screen('receipt', null, refresh_screen); + }, + cancel: function () { + this.gui.show_screen('receipt', null, refresh_screen); + }, + }); + } else if (error.code < 0) { // XmlHttpRequest Errors + this.gui.show_popup('error',{ + 'title': _t('The order could not be sent'), + 'body': _t('Check your internet connection and try again.'), + cancel: function () { + this.gui.show_screen('receipt', {button_print_invoice: true}, refresh_screen); // refresh if necessary + }, + }); + } else if (error.code === 200) { // OpenERP Server Errors + this.gui.show_popup('error-traceback',{ + 'title': error.data.message || _t("Server Error"), + 'body': error.data.debug || _t('The server encountered an error while receiving your order.'), + }); + } else { // ??? + this.gui.show_popup('error',{ + 'title': _t("Unknown Error"), + 'body': _t("The order could not be sent to the server due to an unknown error"), + }); + } + }, }); /*--------------------------------------*\ @@ -1547,7 +1602,9 @@ var ReceiptScreenWidget = ScreenWidget.extend({ return this.pos.config.iface_print_auto && !this.pos.get_order()._printed; }, should_close_immediately: function() { - return this.pos.config.iface_print_via_proxy && this.pos.config.iface_print_skip_screen; + var order = this.pos.get_order(); + var invoiced_finalized = order.is_to_invoice() ? order.finalized : true; + return this.pos.config.iface_print_via_proxy && this.pos.config.iface_print_skip_screen && invoiced_finalized; }, lock_screen: function(locked) { this._locked = locked; @@ -1651,9 +1708,32 @@ var ReceiptScreenWidget = ScreenWidget.extend({ self.print(); } }); + var button_print_invoice = this.$('.button.print_invoice'); + button_print_invoice.click(function () { + var order = self.pos.get_order(); + var invoiced = self.pos.push_and_invoice_order(order); + self.invoicing = true; + + invoiced.fail(self._handleFailedPushForInvoice.bind(self, order, true)); // refresh + + invoiced.done(function(){ + self.invoicing = false; + self.gui.show_screen('receipt', {button_print_invoice: false}, true); // refresh + }); + }); + }, render_change: function() { + var self = this; this.$('.change-value').html(this.format_currency(this.pos.get_order().get_change())); + var order = this.pos.get_order(); + var order_screen_params = order.get_screen_data('params'); + var button_print_invoice = this.$('.button.print_invoice'); + if (order_screen_params && order_screen_params.button_print_invoice) { + button_print_invoice.show(); + } else { + button_print_invoice.hide(); + } }, render_receipt: function() { this.$('.pos-receipt-container').html(QWeb.render('PosTicket', this.get_receipt_render_env())); @@ -2078,45 +2158,7 @@ var PaymentScreenWidget = ScreenWidget.extend({ var invoiced = this.pos.push_and_invoice_order(order); this.invoicing = true; - invoiced.fail(function(error){ - self.invoicing = false; - order.finalized = false; - if (error.message === 'Missing Customer') { - self.gui.show_popup('confirm',{ - 'title': _t('Please select the Customer'), - 'body': _t('You need to select the customer before you can invoice an order.'), - confirm: function(){ - self.gui.show_screen('clientlist'); - }, - }); - } else if (error.message === 'Backend Invoice') { - self.gui.show_popup('confirm',{ - 'title': _t('Please print the invoice from the backend'), - 'body': _t('The order has been synchronized earlier. Please make the invoice from the backend for the order: ') + error.data.order.name, - confirm: function () { - this.gui.show_screen('receipt'); - }, - cancel: function () { - this.gui.show_screen('receipt'); - }, - }); - } else if (error.code < 0) { // XmlHttpRequest Errors - self.gui.show_popup('error',{ - 'title': _t('The order could not be sent'), - 'body': _t('Check your internet connection and try again.'), - }); - } else if (error.code === 200) { // OpenERP Server Errors - self.gui.show_popup('error-traceback',{ - 'title': error.data.message || _t("Server Error"), - 'body': error.data.debug || _t('The server encountered an error while receiving your order.'), - }); - } else { // ??? - self.gui.show_popup('error',{ - 'title': _t("Unknown Error"), - 'body': _t("The order could not be sent to the server due to an unknown error"), - }); - } - }); + invoiced.fail(this._handleFailedPushForInvoice.bind(this, order, false)); invoiced.done(function(){ self.invoicing = false; diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index fe3d86efc21..c791c13f5e8 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -712,6 +712,9 @@ </span> </div> <div class="centered-content touch-scrollable"> + <div class="button print_invoice"> + <i class='fa fa-print'></i> Print Invoice + </div> <div class="button print"> <i class='fa fa-print'></i> Print Receipt </div> diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index 3cdaed5b095..a54ddbce909 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -360,6 +360,9 @@ class ProductTemplate(models.Model): related_vals['volume'] = vals['volume'] if vals.get('weight'): related_vals['weight'] = vals['weight'] + # Please do forward port + if vals.get('packaging_ids'): + related_vals['packaging_ids'] = vals['packaging_ids'] if related_vals: template.write(related_vals) diff --git a/addons/purchase_stock/models/account_invoice.py b/addons/purchase_stock/models/account_invoice.py index 048853d4c64..78cd9ba2707 100644 --- a/addons/purchase_stock/models/account_invoice.py +++ b/addons/purchase_stock/models/account_invoice.py @@ -48,7 +48,6 @@ class AccountInvoice(models.Model): inv.currency_id, company=inv.company_id, date=fields.Date.today(), round=False, ) - line_quantity = line['quantity'] if i_line.product_id.cost_method != 'standard' and i_line.purchase_line_id: po_currency = i_line.purchase_id.currency_id @@ -86,7 +85,6 @@ class AccountInvoice(models.Model): # in Stock Move, price unit is in company_currency valuation_price_unit = valuation_price_unit_total / valuation_total_qty valuation_price_unit = i_line.product_id.uom_id._compute_price(valuation_price_unit, i_line.uom_id) - line_quantity = valuation_total_qty elif i_line.product_id.cost_method == 'fifo': # In this condition, we have a real price-valuated product which has not yet been received @@ -95,7 +93,7 @@ class AccountInvoice(models.Model): company=po_company, date=inv.date or inv.date_invoice, round=False, ) - interim_account_price = valuation_price_unit * line_quantity + interim_account_price = valuation_price_unit * line['quantity'] invoice_cur_prec = inv.currency_id.decimal_places if float_compare(valuation_price_unit, i_line.price_unit, precision_digits=invoice_cur_prec) != 0 and float_compare(line['price_unit'], i_line.price_unit, precision_digits=invoice_cur_prec) == 0: @@ -126,7 +124,7 @@ class AccountInvoice(models.Model): 'type': 'src', 'name': i_line.name[:64], 'price_unit': price_unit_val_dif, - 'quantity': line_quantity, + 'quantity': line['quantity'], 'price': inv.currency_id.round(price_val_dif), 'account_id': acc, 'product_id': line['product_id'], diff --git a/addons/purchase_stock/tests/test_stockvaluation.py b/addons/purchase_stock/tests/test_stockvaluation.py index 73df7e8c22f..8353d7cab2d 100644 --- a/addons/purchase_stock/tests/test_stockvaluation.py +++ b/addons/purchase_stock/tests/test_stockvaluation.py @@ -877,3 +877,244 @@ class TestStockValuationWithCOA(AccountingTestCase): exchange_stock_line = reconciled_lines.filtered(lambda l: l.journal_id == exhange_diff_journal) self.assertEqual(exchange_stock_line.amount_currency, 0.00) self.assertAlmostEqual(exchange_stock_line.balance, 27.86) + + def test_average_realtime_with_two_delivery_anglo_saxon_valuation_multicurrency_different_dates(self): + """ + The PO and invoice are in the same foreign currency. + The deliveries occur at different times and rates + The invoice is created at an even different date + This should create a price difference entry. + """ + company = self.env.user.company_id + company.anglo_saxon_accounting = True + + date_po = '2019-01-01' + date_delivery = '2019-01-08' + date_delivery1 = '2019-01-10' + date_invoice = '2019-01-16' + date_invoice1 = '2019-01-20' + + product_avg = self.product1.product_tmpl_id.copy({ + 'valuation': 'real_time', + 'purchase_method': 'purchase', + 'cost_method': 'average', + 'name': 'AVG', + 'standard_price': 60, + 'property_account_creditor_price_difference': self.price_diff_account.id + }).product_variant_id + product_avg.invoice_policy = 'order' + + # SetUp currency and rates + self.cr.execute("UPDATE res_company SET currency_id = %s WHERE id = %s", (self.usd_currency.id, company.id)) + self.env['res.currency.rate'].search([]).unlink() + self.env['res.currency.rate'].create({ + 'name': date_po, + 'rate': 1.0, + 'currency_id': self.usd_currency.id, + 'company_id': company.id, + }) + self.env['res.currency.rate'].create({ + 'name': date_po, + 'rate': 1.5, + 'currency_id': self.eur_currency.id, + 'company_id': company.id, + }) + + self.env['res.currency.rate'].create({ + 'name': date_delivery, + 'rate': 0.7, + 'currency_id': self.eur_currency.id, + 'company_id': company.id, + }) + self.env['res.currency.rate'].create({ + 'name': date_delivery1, + 'rate': 0.8, + 'currency_id': self.eur_currency.id, + 'company_id': company.id, + }) + + self.env['res.currency.rate'].create({ + 'name': date_invoice, + 'rate': 2, + 'currency_id': self.eur_currency.id, + 'company_id': company.id, + }) + self.env['res.currency.rate'].create({ + 'name': date_invoice1, + 'rate': 2.2, + 'currency_id': self.eur_currency.id, + 'company_id': company.id, + }) + + # To allow testing validation of PO + def _today(*args, **kwargs): + return date_po + # To allow testing validation of Delivery + delivery_now = date_delivery + def _now(*args, **kwargs): + return delivery_now + ' 01:00:00' + + patchers = [ + patch('odoo.fields.Date.context_today', _today), + patch('odoo.fields.Datetime.now', _now), + ] + + for p in patchers: + p.start() + + # Proceed + po = self.env['purchase.order'].create({ + 'currency_id': self.eur_currency.id, + 'partner_id': self.partner_id.id, + 'order_line': [ + (0, 0, { + 'name': product_avg.name, + 'product_id': product_avg.id, + 'product_qty': 10.0, + 'product_uom': product_avg.uom_po_id.id, + 'price_unit': 30.0, + 'date_planned': date_po, + }) + ], + }) + po.button_confirm() + + line_product_avg = po.order_line.filtered(lambda l: l.product_id == product_avg) + + picking = po.picking_ids + (picking.move_lines + .filtered(lambda l: l.purchase_line_id == line_product_avg) + .write({'quantity_done': 5.0})) + + picking.button_validate() + picking.action_done() # Create Backorder + + inv = self.env['account.invoice'].create({ + 'type': 'in_invoice', + 'date_invoice': date_invoice, + 'currency_id': self.eur_currency.id, + 'purchase_id': po.id, + 'partner_id': self.partner_id.id, + 'invoice_line_ids': [ + (0, 0, { + 'name': product_avg.name, + 'price_subtotal': 100.0, + 'price_unit': 20.0, + 'product_id': product_avg.id, + 'purchase_id': po.id, + 'purchase_line_id': line_product_avg.id, + 'quantity': 5.0, + 'account_id': self.stock_input_account.id, + }) + ] + }) + + inv.action_invoice_open() + + backorder_picking = self.env['stock.picking'].search([('backorder_id', '=', picking.id)]) + delivery_now = date_delivery1 + (backorder_picking.move_lines + .filtered(lambda l: l.purchase_line_id == line_product_avg) + .write({'quantity_done': 5.0})) + backorder_picking.button_validate() + + inv1 = self.env['account.invoice'].create({ + 'type': 'in_invoice', + 'date_invoice': date_invoice1, + 'currency_id': self.eur_currency.id, + 'purchase_id': po.id, + 'partner_id': self.partner_id.id, + 'invoice_line_ids': [ + (0, 0, { + 'name': product_avg.name, + 'price_subtotal': 200.0, + 'price_unit': 40.0, + 'product_id': product_avg.id, + 'purchase_id': po.id, + 'purchase_line_id': line_product_avg.id, + 'quantity': 5.0, + 'account_id': self.stock_input_account.id, + }) + ] + }) + + inv1.action_invoice_open() + + for p in patchers: + p.stop() + + ########################## + # Invoice 0 # + ########################## + move_lines = inv.move_id.line_ids + self.assertEqual(len(move_lines), 3) + + # PAYABLE CHECK + payable_line = move_lines.filtered(lambda l: l.account_id.internal_type == 'payable') + self.assertEqual(payable_line.amount_currency, -100.0) + self.assertAlmostEqual(payable_line.balance, -50.00) + + # # PRODUCTS CHECKS + + # DELIVERY DIFFERENCE (AVERAGE) + stock_line = move_lines.filtered(lambda l: l.account_id == self.stock_input_account) + self.assertEqual(stock_line.journal_id, inv.journal_id) + self.assertEqual(stock_line.amount_currency, 150.00) + self.assertAlmostEqual(stock_line.balance, 75.00) + + price_diff_line = move_lines.filtered(lambda l: l.account_id == self.price_diff_account) + self.assertEqual(price_diff_line.amount_currency, -50.00) + self.assertAlmostEqual(price_diff_line.balance, -25.00) + + full_reconcile = stock_line.full_reconcile_id + self.assertTrue(full_reconcile.exists()) + + reconciled_lines = full_reconcile.reconciled_line_ids - stock_line + self.assertEqual(len(reconciled_lines), 2) + + stock_journal_line = reconciled_lines.filtered(lambda l: l.journal_id == self.stock_journal) + self.assertEqual(stock_journal_line.amount_currency, -150) + self.assertAlmostEqual(stock_journal_line.balance, -214.29) + + exhange_diff_journal = company.currency_exchange_journal_id.exists() + exchange_stock_line = reconciled_lines.filtered(lambda l: l.journal_id == exhange_diff_journal) + self.assertEqual(exchange_stock_line.amount_currency, 0.00) + self.assertAlmostEqual(exchange_stock_line.balance, 139.29) + + ########################## + # Invoice 1 # + ########################## + move_lines = inv1.move_id.line_ids + self.assertEqual(len(move_lines), 3) + + # PAYABLE CHECK + payable_line = move_lines.filtered(lambda l: l.account_id.internal_type == 'payable') + self.assertEqual(payable_line.amount_currency, -200.0) + self.assertAlmostEqual(payable_line.balance, -90.91) + + # # PRODUCTS CHECKS + + # DELIVERY DIFFERENCE (AVERAGE) + stock_line = move_lines.filtered(lambda l: l.account_id == self.stock_input_account) + self.assertEqual(stock_line.journal_id, inv.journal_id) + self.assertEqual(stock_line.amount_currency, 150.00) + self.assertAlmostEqual(stock_line.balance, 68.18) + + price_diff_line = move_lines.filtered(lambda l: l.account_id == self.price_diff_account) + self.assertEqual(price_diff_line.amount_currency, 50.00) + self.assertAlmostEqual(price_diff_line.balance, 22.73) + + full_reconcile = stock_line.full_reconcile_id + self.assertTrue(full_reconcile.exists()) + + reconciled_lines = full_reconcile.reconciled_line_ids - stock_line + self.assertEqual(len(reconciled_lines), 2) + + stock_journal_line = reconciled_lines.filtered(lambda l: l.journal_id == self.stock_journal) + self.assertEqual(stock_journal_line.amount_currency, -150) + self.assertAlmostEqual(stock_journal_line.balance, -187.5) + + exhange_diff_journal = company.currency_exchange_journal_id.exists() + exchange_stock_line = reconciled_lines.filtered(lambda l: l.journal_id == exhange_diff_journal) + self.assertEqual(exchange_stock_line.amount_currency, 0.00) + self.assertAlmostEqual(exchange_stock_line.balance, 119.32) diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index 004b085f6ba..361977059aa 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -1095,3 +1095,12 @@ class Picking(models.Model): action['domain'] = [('id', 'in', packages.ids)] action['context'] = {'picking_id': self.id} return action + + def action_picking_move_tree(self): + action = self.env.ref('stock.stock_move_action').read()[0] + action['views'] = [ + (self.env.ref('stock.view_picking_move_tree').id, 'tree'), + ] + action['context'] = self.env.context + action['domain'] = [('picking_id', 'in', self.ids)] + return action diff --git a/addons/stock/views/stock_move_views.xml b/addons/stock/views/stock_move_views.xml index adb5c50923b..635f5d24a5c 100644 --- a/addons/stock/views/stock_move_views.xml +++ b/addons/stock/views/stock_move_views.xml @@ -66,6 +66,38 @@ </field> </record> + <record id="view_picking_move_tree" model="ir.ui.view"> + <field name="name">stock.picking.move.tree</field> + <field name="model">stock.move</field> + <field eval="50" name="priority"/> + <field name="arch" type="xml"> + <tree decoration-danger="state != 'done' and quantity_done > reserved_availability and show_reserved_availability" decoration-muted="scrapped == True or state == 'cancel' or (state == 'done' and is_locked == True)" string="Stock Moves" editable="bottom"> + <field name="name" invisible="1"/> + <field name="date_expected" invisible="1"/> + <field name="state" invisible="1" readonly="0"/> + <field name="picking_type_id" invisible="1"/> + <field name="location_id" invisible="1"/> + <field name="location_dest_id" invisible="1"/> + <field name="scrapped" invisible="1"/> + <field name="picking_code" invisible="1"/> + <field name="product_type" invisible="1"/> + <field name="show_details_visible" invisible="1"/> + <field name="show_reserved_availability" invisible="1"/> + <field name="show_operations" invisible="1" readonly="1"/> + <field name="additional" invisible="1"/> + <field name="has_move_lines" invisible="1"/> + <field name="is_locked" invisible="1"/> + <field name="product_id" required="1" attrs="{'readonly': ['|', '&', ('state', '!=', 'draft'), ('additional', '=', False), ('has_move_lines', '=', True)]}"/> + <field name="is_initial_demand_editable" invisible="1"/> + <field name="is_quantity_done_editable" invisible="1"/> + <field name="product_uom_qty" string="Initial Demand" attrs="{'readonly': [('is_initial_demand_editable', '=', False)]}"/> + <field name="reserved_availability" string="Reserved"/> + <field name="quantity_done" string="Done" attrs="{'readonly': [('is_quantity_done_editable', '=', False)]}"/> + <field name="product_uom" attrs="{'readonly': [('state', '!=', 'draft'), ('additional', '=', False)]}" options="{'no_open': True, 'no_create': True}" string="Unit of Measure" groups="product.group_uom"/> + </tree> + </field> + </record> + <record id="view_move_kandan" model="ir.ui.view"> <field name="name">stock.move.kanban</field> <field name="model">stock.move</field> diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml index bc1265e40e6..29a172115a1 100644 --- a/addons/stock/views/stock_picking_views.xml +++ b/addons/stock/views/stock_picking_views.xml @@ -240,6 +240,19 @@ class="oe_stat_button" icon="fa-cubes" attrs="{'invisible': [('has_packages', '=', False)]}"/> <button name="%(action_stock_report)d" icon="fa-arrow-up" class="oe_stat_button" string="Traceability Report" type="action" attrs="{'invisible': ['|', ('state', '!=', 'done'), ('has_tracking', '=', False)]}" groups="stock.group_production_lot"/> + <!-- Use the following button to avoid onchange on one2many --> + <button name="action_picking_move_tree" + class="oe_stat_button" + icon="fa-arrows-v" + type="object" + help="List view of operations" + groups="base.group_no_one" + attrs="{'invisible': ['|', '&', ('show_operations', '=', True), '|', ('is_locked', '=', True), ('state', '=', 'done'), '&', ('state', '=', 'done'), ('is_locked', '=', True)]}" + context="{'picking_type_code': picking_type_code, 'default_picking_id': id, 'form_view_ref':'stock.view_move_picking_form', 'address_in_id': partner_id, 'default_picking_type_id': picking_type_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}"> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Operations</span> + </div> + </button> </div> <h1 class="d-none d-md-block"> <field name="name" attrs="{'invisible': [('name','=','/')]}" readonly="1"/> diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js index 2b771ba9772..782f31a1bd2 100644 --- a/addons/web/static/src/js/views/calendar/calendar_model.js +++ b/addons/web/static/src/js/views/calendar/calendar_model.js @@ -60,10 +60,11 @@ return AbstractModel.extend({ end.add(-1, 'days'); } + var isDateEvent = this.fields[this.mapping.date_start].type === 'date'; // An "allDay" event without the "all_day" option is not considered // as a 24h day. It's just a part of the day (by default: 7h-19h). if (event.allDay) { - if (!this.mapping.all_day) { + if (!this.mapping.all_day && !isDateEvent) { if (event.r_start) { start.hours(event.r_start.hours()) .minutes(event.r_start.minutes()) diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js index c001cb1001c..ae8184dc7e8 100644 --- a/addons/web/static/tests/views/calendar_tests.js +++ b/addons/web/static/tests/views/calendar_tests.js @@ -2332,6 +2332,67 @@ QUnit.module('Views', { calendar.destroy(); }); + QUnit.test('timzeone does not affect calendar with date field', function (assert) { + assert.expect(8); + + var calendar = createView({ + View: CalendarView, + model: 'event', + data: this.data, + arch: + '<calendar date_start="start_date" mode="month">'+ + '<field name="name"/>'+ + '<field name="start_date"/>'+ + '</calendar>', + archs: archs, + viewOptions: { + initialDate: initialDate, + }, + mockRPC: function (route, args) { + if (args.method === "create") { + assert.strictEqual(args.args[0].start_date, "2016-12-20 00:00:00"); + } + if (args.method === "write") { + assert.step(args.args[1].start_date); + } + return this._super(route, args); + }, + session: { + getTZOffset: function () { + return 120; // 2 hours timezone + }, + }, + }); + + // Create event (on 20 december) + var $cell = calendar.$('.fc-day-grid .fc-row:eq(3) .fc-day:eq(2)'); + testUtils.triggerMouseEvent($cell, "mousedown"); + testUtils.triggerMouseEvent($cell, "mouseup"); + var $input = $('.modal-body input:first'); + $input.val("An event").trigger('input'); + $('.modal button.btn:contains(Create)').trigger('click'); + + assert.strictEqual(calendar.$('.o_field_start_date').text().trim(), "12/20/2016") + + // Move event to another day (on 27 november) + testUtils.dragAndDrop( + calendar.$('.fc-event').first(), + calendar.$('.fc-day-top').first() + ); + assert.verifySteps(["2016-11-27 00:00:00"]); + assert.strictEqual(calendar.$('.o_field_start_date').text().trim(), "11/27/2016") + + // Move event to last day (on 7 january) + testUtils.dragAndDrop( + calendar.$('.fc-event').first(), + calendar.$('.fc-day-top').last() + ); + assert.verifySteps(["2016-11-27 00:00:00", "2017-01-07 00:00:00"]); + assert.strictEqual(calendar.$('.o_field_start_date').text().trim(), "01/07/2017") + + calendar.destroy(); + }); + QUnit.test('form_view_id attribute works (for creating events)', function (assert) { assert.expect(1); diff --git a/addons/website_forum/controllers/main.py b/addons/website_forum/controllers/main.py index db85837d780..84497bbef65 100644 --- a/addons/website_forum/controllers/main.py +++ b/addons/website_forum/controllers/main.py @@ -22,12 +22,8 @@ class WebsiteForum(http.Controller): _user_per_page = 30 def _get_notifications(self): - badge_subtype = request.env.ref('gamification.mt_badge_granted') - if badge_subtype: - msg = request.env['mail.message'].search([('subtype_id', '=', badge_subtype.id), ('needaction', '=', True)]) - else: - msg = list() - return msg + """Deprecated""" + return list() def _prepare_forum_values(self, forum=None, **kwargs): values = { diff --git a/addons/website_hr_recruitment/models/hr_recruitment.py b/addons/website_hr_recruitment/models/hr_recruitment.py index 8606b1826c1..8239fc1e355 100644 --- a/addons/website_hr_recruitment/models/hr_recruitment.py +++ b/addons/website_hr_recruitment/models/hr_recruitment.py @@ -45,7 +45,7 @@ class Job(models.Model): default_description = self.env["ir.model.data"].xmlid_to_object("website_hr_recruitment.default_website_description") return (default_description.render() if default_description else "") - website_description = fields.Html('Website description', translate=html_translate, sanitize_attributes=False, default=_get_default_website_description) + website_description = fields.Html('Website description', translate=html_translate, sanitize_attributes=False, default=_get_default_website_description, prefetch=False) @api.multi def _compute_website_url(self): |
