From 4f9fc188d398f45280ddbf9c61ecfad1ea476e92 Mon Sep 17 00:00:00 2001 From: Jorge Pinna Puissant Date: Tue, 29 Jan 2019 12:34:48 +0000 Subject: [PATCH 01/17] [FIX] stock: Warning in picking on product with serial number In stock.picking, in mode show detailed operation, add a product with unique serial number tracking. Before this commit, a warning about the qty is raised. This warning, must be raised only if the qty is different from 1. However this is not the case, the warning is raised at each modification of the line. This issue occurs since the rounding given to the float_compare is 0.0, because self.move_id.product_id is not set. Float_compare with a 0.0 rounding always return different, hence the bug. Now, the warning is only raised when the qty is different from 1. OPW-1932044 OPW-1931378 closes odoo/odoo#30651 closes odoo/odoo#30851 --- addons/stock/models/stock_move_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 6fe34e6bc96..870de679a38 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -141,7 +141,7 @@ class StockMoveLine(models.Model): """ res = {} if self.qty_done and self.product_id.tracking == 'serial': - if float_compare(self.qty_done, 1.0, precision_rounding=self.move_id.product_id.uom_id.rounding) != 0: + if float_compare(self.qty_done, 1.0, precision_rounding=self.product_id.uom_id.rounding) != 0: message = _('You can only process 1.0 %s for products with unique serial number.') % self.product_id.uom_id.name res['warning'] = {'title': _('Warning'), 'message': message} return res From a21864c36cfc9a10108fc499a6b950bcd6dc5b3a Mon Sep 17 00:00:00 2001 From: Nans Lefebvre Date: Mon, 4 Feb 2019 09:09:08 +0000 Subject: [PATCH 02/17] [FIX] purchase: fix mail templates Activate multi-company, deactivate the common contact book. Create a PO P with user U1 in company A. Switch to company B. User U2 in company A tries to send P by email. Rendering of the template fails, because it tries to access object.create_uid, which breaks multi-company record rules. The signature was not working because it used user_id, a non-existing field, instead of create_uid. opw 1930521 closes odoo/odoo#30868 --- addons/purchase/data/mail_template_data.xml | 15 ++++++++++----- addons/purchase/i18n/purchase.pot | 11 ++++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/addons/purchase/data/mail_template_data.xml b/addons/purchase/data/mail_template_data.xml index ceb19ec9763..65b63e324f7 100644 --- a/addons/purchase/data/mail_template_data.xml +++ b/addons/purchase/data/mail_template_data.xml @@ -5,7 +5,7 @@ RFQ - Send by Email - ${(object.create_uid.email and '"%s" <%s>' % (object.create_uid.name, object.create_uid.email) or '')|safe} + ${(object.sudo().create_uid.email and '"%s" <%s>' % (object.sudo().create_uid.name, object.sudo().create_uid.email) or '')|safe} ${object.company_id.name} Order (Ref ${object.name or 'n/a' }) ${object.partner_id.id} @@ -32,11 +32,11 @@ amounting in ${format_amount(object.amount_total, object.currency_id)} -

Do not hesitate to contact us, further you have any question.

+

Do not hesitate to contact us if you have any further question.

Best regards,

-% if object.user_id and object.user_id.signature: - ${object.user_id.signature | safe} +% if object.sudo().create_uid and object.sudo().create_uid.signature: + ${object.sudo().create_uid.signature | safe} % endif

]]>
@@ -46,7 +46,7 @@ from ${object.company_id.name}. Purchase Order - Send by Email - ${(object.create_uid.email and '"%s" <%s>' % (object.create_uid.name, object.create_uid.email) or '')|safe} + ${(object.sudo().create_uid.email and '"%s" <%s>' % (object.sudo().create_uid.name, object.sudo().create_uid.email) or '')|safe} ${object.company_id.name} Order (Ref ${object.name or 'n/a' }) ${object.partner_id.id} @@ -74,6 +74,11 @@ from ${object.company_id.name}.

You can reply to this email if you have any questions.

Thank you,

+

+% if object.sudo().create_uid and object.sudo().create_uid.signature: + ${object.sudo().create_uid.signature | safe} +% endif +

]]>
diff --git a/addons/purchase/i18n/purchase.pot b/addons/purchase/i18n/purchase.pot index ceea31343a4..c11326da530 100644 --- a/addons/purchase/i18n/purchase.pot +++ b/addons/purchase/i18n/purchase.pot @@ -36,11 +36,11 @@ msgid "\n" "from ${object.company_id.name}.\n" "

\n" "\n" -"

Do not hesitate to contact us, further you have any question.

\n" +"

Do not hesitate to contact us if you have any further question.

\n" "

Best regards,

\n" "

\n" -"% if object.user_id and object.user_id.signature:\n" -" ${object.user_id.signature | safe}\n" +"% if object.sudo().create_uid and object.sudo().create_uid.signature:\n" +" ${object.sudo().create_uid.signature | safe}\n" "% endif\n" "

\n" "" @@ -68,6 +68,11 @@ msgid "\n" "\n" "

You can reply to this email if you have any questions.

\n" "

Thank you,

\n" +"

\n" +"% if object.sudo().create_uid and object.sudo().create_uid.signature:\n" +" ${object.sudo().create_uid.signature | safe}\n" +"% endif\n" +"

\n" "" msgstr "" From d89f4d6ec26806389922351eb8e575754b3f60e0 Mon Sep 17 00:00:00 2001 From: "Atchuthan, Sodexis" Date: Thu, 24 May 2018 10:22:28 +0000 Subject: [PATCH 03/17] [FIX] stock: set picking_type_id to readonly except in draft state Before this commit, the customer is allowed to change picking type after the stock.picking record is moved from draft. But it doesn't make the changes in the stock moves and operations as the procurements are already created for initial demand. To avoid confusing the user, it can now only be modified in draft state. Authored by SodexisTeam opw 1848252 opw-1934814 closes odoo/odoo#30865 --- addons/stock/models/stock_picking.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index 3e3e99c931e..c1f5f98ba09 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -241,7 +241,8 @@ class Picking(models.Model): picking_type_id = fields.Many2one( 'stock.picking.type', 'Picking Type', required=True, - states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}) + readonly=True, + states={'draft': [('readonly', False)]}) picking_type_code = fields.Selection([ ('incoming', 'Vendors'), ('outgoing', 'Customers'), From c7c938996f783cbb187697b9f36c8359b2c4733f Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 6 Feb 2019 10:24:09 +0000 Subject: [PATCH 04/17] [FIX] account: analytic cost - Set a cost of 12.34 for product P - Create manually a new analytic entry - Choose product P - Set desired quantity The analytic amount is set to 12.00. This is because `move_id` is empty, therefore `currency_id` is empty as well and `decimal_places` is zero opw-1924184 closes odoo/odoo#30869 --- addons/account/models/account_analytic_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/models/account_analytic_line.py b/addons/account/models/account_analytic_line.py index 20b44c04dbb..5f001ad34dd 100644 --- a/addons/account/models/account_analytic_line.py +++ b/addons/account/models/account_analytic_line.py @@ -44,7 +44,7 @@ class AccountAnalyticLine(models.Model): # Compute based on pricetype amount_unit = self.product_id.price_compute('standard_price', uom=unit)[self.product_id.id] amount = amount_unit * self.unit_amount or 0.0 - result = round(amount, self.currency_id.decimal_places) * -1 + result = round(amount, self.currency_id.decimal_places if self.currency_id else 2) * -1 self.amount = result self.general_account_id = account self.product_uom_id = unit From 845a6a9e08293a955127ec587397bbf7bec27116 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 1 Feb 2019 17:33:50 +0000 Subject: [PATCH 05/17] [FIX] stock: lot in multiple locations - Activate: 'Track lots or serial numbers' 'Manage several Warehouses, each one composed by several stock locations' 'Advanced routing of products using rules' - In the main warehouse, activate Pick + Ship - In Stock Locations, create 'WH/Stock/Shelf 3' and 'WH/Stock/Shelf 4' (1 and 2 already exist, use the same config) - Create a new product 'Foo', activate Tracking By Lots - On Foo, use 4 times the "Update Qty on Hand" (order is important): Add 1 on Shelf 2, create a lot Add 1 on Shelf 1, use the same lot Add 3 on Shelf 4, use the same lot Add 1 on Shelf 3, use the same lot - Create a SO, set a partner - Ensure that YourCompany is used as warehouse - Add several SO lines (order is important) a line with 1 product Foo a second line with 1 product Foo a third line with 4 products Foo - Confirm the SO, you now have 2 deliveries, 1 Pick, 1 Out - Open the Pick, all moves should be available, you should have: 4 operations: Shelf 2, quantity 1 Shelf 4, quantity 3 Shelf 1, quantity 1 Shelf 3, quantity 1 3 moves: A, quantity 1 B, quantity 1 C, quantity 4 - On each operation, select the lot created before and set the quantity to be fully done - Validate the Pick The moves end up like this: - Move A with qty 1: is done and linked with a quant of 1 - Move B with qty 1: is done and linked with no quant - Move C with qty 4: is done and linked with a quant of 1, a quant of 3, a quant of 1 (sum is 5) In the Out picking, the move linked with the source move (B) with no quant stays in "Waiting another move" even if the source move is done. This outgoing move will never be available. In the method `recompute_remaining_qty`, we loop on operations, and match them wih the moves. However, the operation with the largest quantity (Shelf 4, quantity 3) is processed before the move with the highest quantity (C, quantity 4). Therefore, when we later loop on move C, `qty_assign_cmp` is larger than zero, which sets `need_rereserve` and ultimately triggers `rereserve_quants` in `do_transfer`. A first part of the fix is to check for the location of the quants when matching moves and operations. This fixes the original issue, but inconsistencies can still arise since a quant which is taken partially. therefore, we make sure to never take more than the quantity on the link. opw-1932624 closes odoo/odoo#30857 --- addons/stock/models/stock_move.py | 14 ++++++++------ addons/stock/models/stock_picking.py | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index c170d2cc5fa..43a51a03831 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -868,13 +868,15 @@ class StockMove(models.Model): false_quants += [reserved_quant] elif float_compare(lot_quantities.get(reserved_quant.lot_id.id, 0), 0, precision_rounding=rounding) > 0: if float_compare(lot_quantities[reserved_quant.lot_id.id], reserved_quant.qty, precision_rounding=rounding) >= 0: - lot_quantities[reserved_quant.lot_id.id] -= reserved_quant.qty - quants_taken += [(reserved_quant, reserved_quant.qty)] - qty_on_link -= reserved_quant.qty + qty_taken = min(reserved_quant.qty, qty_on_link) + lot_quantities[reserved_quant.lot_id.id] -= qty_taken + quants_taken += [(reserved_quant, qty_taken)] + qty_on_link -= qty_taken else: - quants_taken += [(reserved_quant, lot_quantities[reserved_quant.lot_id.id])] - lot_quantities[reserved_quant.lot_id.id] = 0 - qty_on_link -= lot_quantities[reserved_quant.lot_id.id] + qty_taken = min(qty_on_link, lot_quantities[reserved_quant.lot_id.id]) + quants_taken += [(reserved_quant, qty_taken)] + lot_quantities[reserved_quant.lot_id.id] -= qty_taken + qty_on_link -= qty_taken lot_move_qty[move.id] = qty_on_link remaining_move_qty[move.id] -= prout_move_qty[move] diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index c1f5f98ba09..3d381ac064c 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -737,7 +737,7 @@ class Picking(models.Model): flag = quant.package_id == ops.package_id else: flag = not quant.package_id.id - flag = flag and (ops.owner_id.id == quant.owner_id.id) + flag = flag and (ops.owner_id.id == quant.owner_id.id) and (ops.location_id.id == quant.location_id.id) if flag: if not lot_qty: max_qty_on_link = min(quant.qty, qty_to_assign) From 80460c40d9785f3e24fbc9464cc7e117f56f446f Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Wed, 6 Feb 2019 10:34:57 +0000 Subject: [PATCH 06/17] [FIX] account, purchase, sale: notification with right lang Have a partner in French while you operate in English Click on 'send by email' on a invoice/sale order/purchase order Before this commit: - The body of the mail is translated in the partner's language - but the notification header/footer surrounding the body was not This is because: - mail templates apply to a model - in the case of the notification header/footer, this model is 'mail.message' - mail.message doesn't have a lang field - at the notification rendering time, we don't have easy (and generic) access to the "real" model or to the lang (let alone to which lang to apply) that we need After this commit, the notification is translated but: the lang to get is hardcoded, and is the one on the partner of the inv/so/po and is un-coupled from the dynamic lang on the proper inv/so/po template This patch is hackish at several levels, I suggest that a generic solution is found in master If no other solution is found until then, then the patch may be forward-ported OPW 1935091 closes odoo/odoo#30873 --- addons/account/models/account_invoice.py | 2 +- addons/purchase/models/purchase.py | 3 ++- addons/sale/wizard/mail_compose_message.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 5f2d83c5035..576f9d9e551 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -1834,5 +1834,5 @@ class MailComposeMessage(models.TransientModel): invoice = self.env['account.invoice'].browse(context['default_res_id']) if not invoice.sent: invoice.sent = True - self = self.with_context(mail_post_autofollow=True) + self = self.with_context(mail_post_autofollow=True, lang=invoice.partner_id.lang) return super(MailComposeMessage, self).send_mail(auto_commit=auto_commit) diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 53eeaffa2e3..8962513fd02 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -1131,6 +1131,7 @@ class MailComposeMessage(models.TransientModel): @api.multi def send_mail(self, auto_commit=False): if self._context.get('default_model') == 'purchase.order' and self._context.get('default_res_id'): - self = self.with_context(mail_post_autofollow=True) + order = self.env['purchase.order'].browse(self._context['default_res_id']) + self = self.with_context(mail_post_autofollow=True, lang=order.partner_id.lang) self.mail_purchase_order_on_send() return super(MailComposeMessage, self).send_mail(auto_commit=auto_commit) diff --git a/addons/sale/wizard/mail_compose_message.py b/addons/sale/wizard/mail_compose_message.py index 9ec46b48308..0291d3f848c 100644 --- a/addons/sale/wizard/mail_compose_message.py +++ b/addons/sale/wizard/mail_compose_message.py @@ -13,5 +13,5 @@ class MailComposeMessage(models.TransientModel): order = self.env['sale.order'].browse([self._context['default_res_id']]) if order.state == 'draft': order.with_context(tracking_disable=True).state = 'sent' - self = self.with_context(mail_post_autofollow=True) + self = self.with_context(mail_post_autofollow=True, lang=order.partner_id.lang) return super(MailComposeMessage, self).send_mail(auto_commit=auto_commit) From fa97d6ab1573515e6485b169df1e1f6b709275de Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 6 Feb 2019 14:11:50 +0000 Subject: [PATCH 07/17] [FIX] purchase: supplier on parent company - Create a product and set a contact as the vendor (for instance, Arthur Gomez from Asustek in the runbot) - Set a specific name and code for the supplier - Create a RFQ for this vendor (Arthur Gomez - the contact person) - Add the product, confirm the RFQ On the product, the company (Asustek) is automatically added as vendor, but there is no vendor name and code. opw-1929745 closes odoo/odoo#30891 --- addons/purchase/models/purchase.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 295c434c4e2..8f80e112b32 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -448,6 +448,16 @@ class PurchaseOrder(models.Model): 'currency_id': currency.id, 'delay': 0, } + # In case the order partner is a contact address, a new supplierinfo is created on + # the parent company. In this case, we keep the product name and code. + seller = line.product_id._select_seller( + partner_id=line.partner_id, + quantity=line.product_qty, + date=line.order_id.date_order and line.order_id.date_order[:10], + uom_id=line.product_uom) + if seller: + supplierinfo['product_name'] = seller.product_name + supplierinfo['product_code'] = seller.product_code vals = { 'seller_ids': [(0, 0, supplierinfo)], } From 2a881865e88ad3c1f7ca52f0ec4e61b8745c3eec Mon Sep 17 00:00:00 2001 From: Carel Date: Wed, 6 Feb 2019 10:52:00 +0000 Subject: [PATCH 08/17] [CLA] signature for Carel van Dam closes odoo/odoo#30876 --- doc/cla/individual/carelvd.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/cla/individual/carelvd.md diff --git a/doc/cla/individual/carelvd.md b/doc/cla/individual/carelvd.md new file mode 100644 index 00000000000..e4570a18845 --- /dev/null +++ b/doc/cla/individual/carelvd.md @@ -0,0 +1,11 @@ +South Africa, 05/02/2019 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Carel van Dam carelvdam@gmail.com https://github.com/carelvd From 37220d02a4d86e0f9a1d4658770a2d673605de13 Mon Sep 17 00:00:00 2001 From: Jorge Pinna Puissant Date: Fri, 1 Feb 2019 13:15:04 +0000 Subject: [PATCH 09/17] [FIX] payment_authorize: add new card verification In the e-comerce, when the user try to add a new card in this payment methods. Before this commit, to validate the card, besides of the creation of the payment token, an amount of $1.5 id charged and refunded. During the refunded an error was raised. This error arrives because: payments made via Authorize.net are settled and allowed to be refunded only on the next day. https://account.authorize.net/help/Miscellaneous/FAQ/Frequently_Asked_Questions.htm#Refund The original transaction that you wish to refund must have a status of Settled Successfully. You cannot issue refunds against unsettled, voided, declined or errored transactions. This means, we can't refund directly after the payment. Now, we only create the payment token. As Authorize.net verify the card during the creation of the token, a new verification is not needed. OPW-1927754 closes odoo/odoo#30905 --- addons/payment_authorize/controllers/main.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/payment_authorize/controllers/main.py b/addons/payment_authorize/controllers/main.py index 9614327401a..a5f55f07afe 100644 --- a/addons/payment_authorize/controllers/main.py +++ b/addons/payment_authorize/controllers/main.py @@ -77,13 +77,14 @@ class AuthorizeController(http.Controller): 'id': token.id, 'short_name': token.short_name, '3d_secure': False, - 'verified': False, + 'verified': True, #Authorize.net does a transaction type of Authorization Only + #As Authorize.net already verify this card, we do not verify this card again. } - - if verify_validity != False: - token.validate() - res['verified'] = token.verified - + #token.validate() don't work with Authorize.net. + #Payments made via Authorize.net are settled and allowed to be refunded only on the next day. + #https://account.authorize.net/help/Miscellaneous/FAQ/Frequently_Asked_Questions.htm#Refund + #The original transaction that you wish to refund must have a status of Settled Successfully. + #You cannot issue refunds against unsettled, voided, declined or errored transactions. return res @http.route(['/payment/authorize/s2s/create'], type='http', auth='public') From c47dc928f6ec67f0e76c8b7907d7f62ae2452d89 Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Tue, 5 Feb 2019 15:05:34 +0000 Subject: [PATCH 10/17] [FIX] stock: warehouse_selectable skip warehouse_ids Usecase to reproduce: - Create a route with a warehouse_ids - Uncheck warehouse_selectable on route - Create a picking that use the route It should fail since the route is not applicable. It happens because _search_rule only check for route with warehouse_ids but it do not check for warehouse_selectable. We can't fix _search_rule since stock_warehouse.py creates multi step delivery and reception routes without warehouse_selectable thus it would break existing configuration. Instead create an onchange on warehouse_selectable that remove warehouse_ids on route. partial backport of commit a35df8d37170790257876a54aa54f2c3ab596c44 closes odoo/odoo#30844 --- addons/stock/models/stock_location.py | 5 +++++ addons/stock/models/stock_warehouse.py | 1 + 2 files changed, 6 insertions(+) diff --git a/addons/stock/models/stock_location.py b/addons/stock/models/stock_location.py index bce2de0ecd2..b3903df88b7 100644 --- a/addons/stock/models/stock_location.py +++ b/addons/stock/models/stock_location.py @@ -170,6 +170,11 @@ class Route(models.Model): categ_ids = fields.Many2many('product.category', 'stock_location_route_categ', 'route_id', 'categ_id', 'Product Categories') warehouse_ids = fields.Many2many('stock.warehouse', 'stock_route_warehouse', 'route_id', 'warehouse_id', 'Warehouses') + @api.onchange('warehouse_selectable') + def _onchange_warehouse_selectable(self): + if not self.warehouse_selectable: + self.warehouse_ids = [] + def write(self, values): '''when a route is deactivated, deactivate also its pull and push rules''' res = super(Route, self).write(values) diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index 3394c3a1e7b..dc8766954de 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -467,6 +467,7 @@ class Warehouse(models.Model): 'name': self._format_routename(route_type=route_type), 'product_categ_selectable': True, 'product_selectable': False, + 'warehouse_selectable': True, 'company_id': self.company_id.id, 'sequence': 10, } From f9a80a6e84ec3dd9e47f547e44c3bce1d134bda0 Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Thu, 24 Jan 2019 16:39:41 +0000 Subject: [PATCH 11/17] [FIX] web: calendar: take locale to format dates Backport of d9fed5381a78c19ce14ddc8b9233f60d53c65453 Before this commit, when opening the calendar view with a specific locale in the "week" view the days were translated but the date format was wrong and fell back to english This was because the translated terms were passed explicitly, but the locale did not get passed After this commit, we do what it takes to pass the locale to fullcalendar and the dates are formatted with the right pattern Also, there may be a bug in fullcalendar, because just passing the locale in the options won't work, it should be instanciated first in fullcalendar's "locales cache" OPW 1922092 OPW 1934127 closes odoo/odoo#30909 --- .../js/views/calendar/calendar_renderer.js | 6 +++ .../web/static/tests/views/calendar_tests.js | 48 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js index 6565cbb8385..4c0fabadfdf 100644 --- a/addons/web/static/src/js/views/calendar/calendar_renderer.js +++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js @@ -317,6 +317,11 @@ return AbstractRenderer.extend({ this.$calendar = this.$(".o_calendar_widget"); + // This seems like a workaround but apparently passing the locale + // in the options is not enough. We should initialize it beforehand + var locale = moment.locale(); + $.fullCalendar.locale(locale); + //Documentation here : http://arshaw.com/fullcalendar/docs/ var fc_options = $.extend({}, this.state.fc_options, { eventDrop: function (event) { @@ -370,6 +375,7 @@ return AbstractRenderer.extend({ }, height: 'parent', unselectAuto: false, + locale: locale, // reset locale when fullcalendar has already been instanciated before now }); this.$calendar.fullCalendar(fc_options); diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js index 00cdfa24dd2..fad84cc43c3 100644 --- a/addons/web/static/tests/views/calendar_tests.js +++ b/addons/web/static/tests/views/calendar_tests.js @@ -6,6 +6,7 @@ var CalendarRenderer = require('web.CalendarRenderer'); var fieldUtils = require('web.field_utils'); var mixins = require('web.mixins'); var testUtils = require('web.test_utils'); +var time = require('web.time'); var session = require('web.session'); @@ -2146,6 +2147,53 @@ QUnit.module('Views', { calendar.destroy(); }); + QUnit.test('fullcalendar initializes with right locale', function (assert) { + assert.expect(1); + + // Do not forward port this part in versions >= saas11.3 + testUtils.patch(time, { + getLangDateFormat: function () { + return 'DD/MM'; + }, + }); + + var initialLocale = moment.locale(); + // This will set the locale to zz + moment.defineLocale('zz', { + longDateFormat: { + L: 'DD/MM/YYYY' + }, + weekdaysShort: ["zz1.", "zz2.", "zz3.", "zz4.", "zz5.", "zz6.", "zz7."], + }); + + var calendar = createView({ + View: CalendarView, + model: 'event', + data: this.data, + arch: ' '+ + ''+ + '', + archs: archs, + viewOptions: { + initialDate: initialDate, + action: {views: [{viewID: 1, type: 'kanban'}, {viewID: 43, type: 'form'}]} + }, + + }); + + assert.strictEqual(calendar.$('.fc-day-header:first').text(), "zz1. 11/12", + 'The day should be in the given locale specific format'); + + moment.locale(initialLocale); + // Do not forward port this part in versions >= saas11.3 + testUtils.unpatch(time); + + calendar.destroy(); + }); + }); }); From 0757ba3c639000edba89448a3a542a9f9c225538 Mon Sep 17 00:00:00 2001 From: RomainLibert Date: Thu, 7 Feb 2019 15:25:13 +0000 Subject: [PATCH 12/17] [FIX] fleet: fix _get_default_state method If fleet.vehicle_state_active is not found in the system then the method will raise an error, hence it won't allow creating any fleet.vehicle closes odoo/odoo#30925 --- addons/fleet/models/fleet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/fleet/models/fleet.py b/addons/fleet/models/fleet.py index 364b44fff16..538142ad2bb 100644 --- a/addons/fleet/models/fleet.py +++ b/addons/fleet/models/fleet.py @@ -143,7 +143,7 @@ class FleetVehicle(models.Model): def _get_default_state(self): state = self.env.ref('fleet.vehicle_state_active', raise_if_not_found=False) - return state and state.id or False + return state if state and state.id else False name = fields.Char(compute="_compute_vehicle_name", store=True) active = fields.Boolean(default=True) From be689c53c13330832510c8cef332ac862d4ed5e8 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Wed, 6 Feb 2019 17:25:36 +0000 Subject: [PATCH 13/17] [FIX] web_editor: review drag and drop in the editor When dropping a snippet into a page, it is dropped in the drop-zone which is the nearest of the user cursor. When moving a snippet, that condition did not apply and the user was required to put the cursor at the exact location of the drop-zone. Also, for both drag and drop features, the drop zones which appeared were not displayed correctly for full width columns, which made dropping sometimes impossible when multiple col-*-12 were below each other. task-1937758 closes odoo/odoo#30899 --- .../static/src/js/snippets.editor.js | 36 ++++++++++++++++--- .../static/src/less/web_editor.ui.less | 4 +-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/addons/web_editor/static/src/js/snippets.editor.js b/addons/web_editor/static/src/js/snippets.editor.js index 97830d2ae8e..17c0c2ffb85 100644 --- a/addons/web_editor/static/src/js/snippets.editor.js +++ b/addons/web_editor/static/src/js/snippets.editor.js @@ -615,6 +615,10 @@ data.Class = Widget.extend({ var self = this; var zone_template = $("
"); + function isFullWidth($elem) { + return $elem.parent().width() === $elem.outerWidth(true); + } + if ($selector_children) { $selector_children.each(function () { var $zone = $(this); @@ -632,7 +636,10 @@ data.Class = Widget.extend({ 'display': 'inline-block' }); } else if (float === "left" || float === "right") { - $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.children().last().outerHeight()), 30)); + $drop.css('float', float); + if (!isFullWidth($zone)) { + $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.children().last().outerHeight()), 30)); + } } $drop = $drop.clone(); @@ -647,7 +654,10 @@ data.Class = Widget.extend({ 'display': 'inline-block' }); } else if (float === "left" || float === "right") { - $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.children().first().outerHeight()), 30)); + $drop.css('float', float); + if (!isFullWidth($zone)) { + $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.children().first().outerHeight()), 30)); + } } if (test) { $drop.css({'float': 'none', 'display': 'inline-block'}); @@ -668,14 +678,20 @@ data.Class = Widget.extend({ if($zone.prev('.oe_drop_zone:visible').length === 0) { $drop = zone_template.clone(); if (float === "left" || float === "right") { - $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.prev().outerHeight() || Infinity), 30)); + $drop.css('float', float); + if (!isFullWidth($zone)) { + $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.prev().outerHeight() || Infinity), 30)); + } } $zone.before($drop); } if($zone.next('.oe_drop_zone:visible').length === 0) { $drop = zone_template.clone(); if (float === "left" || float === "right") { - $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.next().outerHeight() || Infinity), 30)); + $drop.css('float', float); + if (!isFullWidth($zone)) { + $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.next().outerHeight() || Infinity), 30)); + } } $zone.after($drop); } @@ -862,6 +878,7 @@ data.Editor = Class.extend({ }, _drag_and_drop_start: function () { var self = this; + this.dropped = false; self.buildingBlock.editor_busy = true; self.size = { width: self.$target.width(), @@ -890,9 +907,18 @@ data.Editor = Class.extend({ self._drag_and_drop_after_insert_dropzone(); self._drag_and_drop_active_drop_zone($('.oe_drop_zone')); }, - _drag_and_drop_stop: function () { + _drag_and_drop_stop: function (ev, ui) { var self = this; + // TODO lot of this is duplicated code of the d&d feature of snippets + if (!this.dropped) { + var $el = $.nearest({x: ui.position.left, y: ui.position.top}, '.oe_drop_zone').first(); + if ($el.length) { + $el.after(this.$target); + this.dropped = true; + } + } + $(".oe_drop_zone").droppable('destroy').remove(); var prev = this.$target.first()[0].previousSibling; diff --git a/addons/web_editor/static/src/less/web_editor.ui.less b/addons/web_editor/static/src/less/web_editor.ui.less index 02094adf754..965a0ca820e 100644 --- a/addons/web_editor/static/src/less/web_editor.ui.less +++ b/addons/web_editor/static/src/less/web_editor.ui.less @@ -264,12 +264,12 @@ html[lang] > body.editor_enable [data-oe-translation-state] { } &.oe_vertical { - border: 2px dashed @odoo-brand-primary; + border: @odoo-dropzone-border; border-left: none; border-right: none; width: @odoo-dropzone-size; - float: left; + height: 100%; margin: 0 -@odoo-dropzone-size/2; &:after { From a02e2778172f7de54cca58babb26151030b5b9dc Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Mon, 2 Jul 2018 08:38:31 +0000 Subject: [PATCH 14/17] [IMP] account: keep numbering of reset-to-draft payments Was github issue #25441 opw-1935427 closes odoo/odoo#30918 --- addons/account/models/account_payment.py | 38 +++++++++++++----------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index 0e19cd13b55..3bca6023403 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -276,7 +276,7 @@ class account_payment(models.Model): self.payment_difference = self._compute_total_invoices_amount() - self.amount company_id = fields.Many2one(store=True) - name = fields.Char(readonly=True, copy=False, default="Draft Payment") # The name is attributed upon post() + name = fields.Char(readonly=True, copy=False) # The name is attributed upon post() state = fields.Selection([('draft', 'Draft'), ('posted', 'Posted'), ('sent', 'Sent'), ('reconciled', 'Reconciled'), ('cancelled', 'Cancelled')], readonly=True, default='draft', copy=False, string="Status") payment_type = fields.Selection(selection_add=[('transfer', 'Internal Transfer')]) @@ -494,23 +494,25 @@ class account_payment(models.Model): if any(inv.state != 'open' for inv in rec.invoice_ids): raise ValidationError(_("The payment cannot be processed because the invoice is not open!")) - # Use the right sequence to set the name - if rec.payment_type == 'transfer': - sequence_code = 'account.payment.transfer' - else: - if rec.partner_type == 'customer': - if rec.payment_type == 'inbound': - sequence_code = 'account.payment.customer.invoice' - if rec.payment_type == 'outbound': - sequence_code = 'account.payment.customer.refund' - if rec.partner_type == 'supplier': - if rec.payment_type == 'inbound': - sequence_code = 'account.payment.supplier.refund' - if rec.payment_type == 'outbound': - sequence_code = 'account.payment.supplier.invoice' - rec.name = self.env['ir.sequence'].with_context(ir_sequence_date=rec.payment_date).next_by_code(sequence_code) - if not rec.name and rec.payment_type != 'transfer': - raise UserError(_("You have to define a sequence for %s in your company.") % (sequence_code,)) + # keep the name in case of a payment reset to draft + if not rec.name: + # Use the right sequence to set the name + if rec.payment_type == 'transfer': + sequence_code = 'account.payment.transfer' + else: + if rec.partner_type == 'customer': + if rec.payment_type == 'inbound': + sequence_code = 'account.payment.customer.invoice' + if rec.payment_type == 'outbound': + sequence_code = 'account.payment.customer.refund' + if rec.partner_type == 'supplier': + if rec.payment_type == 'inbound': + sequence_code = 'account.payment.supplier.refund' + if rec.payment_type == 'outbound': + sequence_code = 'account.payment.supplier.invoice' + rec.name = self.env['ir.sequence'].with_context(ir_sequence_date=rec.payment_date).next_by_code(sequence_code) + if not rec.name and rec.payment_type != 'transfer': + raise UserError(_("You have to define a sequence for %s in your company.") % (sequence_code,)) # Create the journal entry amount = rec.amount * (rec.payment_type in ('outbound', 'transfer') and 1 or -1) From f5107b10f5658f51c5cc07c6f3b7e7b0f38bf8d3 Mon Sep 17 00:00:00 2001 From: Nans Lefebvre Date: Thu, 7 Feb 2019 21:45:30 +0000 Subject: [PATCH 15/17] [FIX] mrp: fix singleton error in _update_raw_move Commit bfb0f1e2a5d3a921bef89718e06c5f68da4f43ac did a very good job not seeing that the lines only used move[0], and called move.field directly. This could result, for certain BoMs, in traceback. opw 1928677 closes odoo/odoo#30938 --- addons/mrp/models/mrp_production.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 45eb460a25b..0adfcea608a 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -455,7 +455,7 @@ class MrpProduction(models.Model): move[0].with_context(do_not_unreserve=True).write({'product_uom_qty': quantity}) move[0]._recompute_state() move[0]._action_assign() - move.unit_factor = quantity / move.raw_material_production_id.product_qty + move[0].unit_factor = quantity / move[0].raw_material_production_id.product_qty elif quantity < 0: # Do not remove 0 lines 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. ')) From d9f9d32f4e7d2408ee6245238a82b24e1fac864f Mon Sep 17 00:00:00 2001 From: Alex Tuyls Date: Thu, 7 Feb 2019 14:14:37 +0000 Subject: [PATCH 16/17] [FIX] sale_mrp: incorrect search of BOM in multicompany In following configuration: - We have two companies: A and B - We have a shared product X - We set a BOM of type KIT for this product X for company B only (the field company on a BOM is mandatory) - We set no BOM for product X for company A because in company A we sell the product as a regular product When we sell product X in company A, as a normal stockable product, the invoicing behavior is the same as when we would sell KIT products. This is because we don't check the SO company when searching the associated BOM. closes odoo/odoo#30927 --- addons/sale_mrp/models/sale_mrp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale_mrp/models/sale_mrp.py b/addons/sale_mrp/models/sale_mrp.py index 9b33c89f57c..68600642c9b 100644 --- a/addons/sale_mrp/models/sale_mrp.py +++ b/addons/sale_mrp/models/sale_mrp.py @@ -14,7 +14,7 @@ class SaleOrderLine(models.Model): # In the case of a kit, we need to check if all components are shipped. Since the BOM might # have changed, we don't compute the quantities but verify the move state. - bom = self.env['mrp.bom']._bom_find(product=self.product_id) + bom = self.env['mrp.bom']._bom_find(product=self.product_id,company_id=self.company_id.id) if bom and bom.type == 'phantom': moves = self.move_ids.filtered(lambda m: m.picking_id and m.picking_id.state != 'cancel') bom_delivered = all([move.state == 'done' for move in moves]) From d260c53d12180bf3c2f4f06cf4cdcfb554fd4ba5 Mon Sep 17 00:00:00 2001 From: Florent de Labarre Date: Tue, 5 Feb 2019 13:24:00 +0000 Subject: [PATCH 17/17] [FIX] l10n_fr_fec: wrong formatting The exported CSV contains unexpected `b` characters. opw-1937282 closes odoo/odoo#30838 --- addons/l10n_fr_fec/wizard/account_fr_fec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py index bd8e4cc44c0..032baf2d971 100644 --- a/addons/l10n_fr_fec/wizard/account_fr_fec.py +++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py @@ -265,7 +265,7 @@ class AccountFrFec(models.TransientModel): for row in self._cr.fetchall(): listrow = list(row) account_id = listrow.pop() - w.writerow([s.encode("utf-8") for s in listrow]) + w.writerow(listrow) # LINES sql_query = '''