Skip to content
Permalink
Browse files

[MERGE] forward port branch 12.0 up to 32039b2

  • Loading branch information...
KangOl committed Aug 19, 2019
2 parents 57fef7c + 32039b2 commit 168e54d48850418a6a83f71427e74ed27f66595b
Showing with 555 additions and 137 deletions.
  1. +2 −2 addons/account/models/account_invoice.py
  2. +23 −13 addons/account/models/account_move.py
  3. +4 −1 addons/account/models/account_payment.py
  4. +2 −0 addons/account/models/account_reconcile_model.py
  5. +1 −1 addons/account/views/res_config_settings_views.xml
  6. +1 −0 addons/base_iban/models/res_partner_bank.py
  7. +3 −0 addons/base_import/models/base_import.py
  8. +15 −0 addons/base_vat/models/res_partner.py
  9. +3 −3 addons/calendar/views/calendar_views.xml
  10. +1 −1 addons/crm/views/crm_lead_views.xml
  11. +1 −1 addons/event/views/event_views.xml
  12. +1 −1 addons/hr_contract/views/hr_contract_views.xml
  13. +11 −14 addons/hw_escpos/escpos/escpos.py
  14. +4 −2 addons/iap/models/iap.py
  15. +11 −0 addons/l10n_ae/data/account_tax_template_data.xml
  16. +3 −3 addons/l10n_it_edi/models/account_invoice.py
  17. +1 −1 addons/l10n_it_edi/models/ir_mail_server.py
  18. +5 −5 addons/l10n_it_edi/models/res_company.py
  19. +7 −7 addons/mail/models/mail_channel.py
  20. +2 −2 addons/mail/models/mail_thread.py
  21. +0 −1 addons/mail/static/src/js/models/threads/document_thread.js
  22. +65 −0 addons/mail/static/tests/chatter_tests.js
  23. +1 −1 addons/mail/wizard/mail_resend_message.py
  24. +1 −1 addons/maintenance/models/maintenance.py
  25. +2 −2 addons/maintenance/views/maintenance_views.xml
  26. +1 −1 addons/mass_mailing/models/mass_mailing.py
  27. +3 −3 addons/mass_mailing/views/mass_mailing_views.xml
  28. +1 −1 addons/mass_mailing/views/res_config_settings_views.xml
  29. +5 −0 addons/mrp/models/mrp_workorder.py
  30. +1 −1 addons/note/views/note_views.xml
  31. +1 −1 addons/partner_autocomplete/models/res_partner.py
  32. +4 −1 addons/point_of_sale/static/src/js/screens.js
  33. +1 −1 addons/point_of_sale/wizard/pos_details.xml
  34. +3 −3 addons/portal/controllers/portal.py
  35. +2 −2 addons/pos_mercury/views/pos_config_setting_views.xml
  36. +6 −1 addons/pos_mercury/views/pos_config_views.xml
  37. +1 −1 addons/project/views/project_views.xml
  38. +2 −0 addons/purchase/models/purchase.py
  39. +2 −1 addons/purchase_stock/models/stock.py
  40. +62 −0 addons/purchase_stock/tests/test_create_picking.py
  41. +1 −1 addons/sale/models/sale.py
  42. +3 −3 addons/sale_management/models/sale_order_template.py
  43. +3 −2 addons/sale_management/views/sale_order_views.xml
  44. +2 −1 addons/sale_stock/models/stock.py
  45. +1 −1 addons/sale_timesheet/models/account_invoice.py
  46. +2 −1 addons/sale_timesheet/models/sale_order.py
  47. +1 −9 addons/stock/models/res_config_settings.py
  48. +1 −1 addons/stock/models/stock_production_lot.py
  49. +2 −2 addons/stock_account/models/stock.py
  50. +1 −1 addons/stock_picking_batch/report/report_picking_batch.xml
  51. +1 −1 addons/survey/views/survey_survey_views.xml
  52. +1 −1 addons/web/static/src/js/core/utils.js
  53. +3 −3 addons/web/static/src/js/views/basic/basic_model.js
  54. +36 −8 addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
  55. +31 −0 addons/web/static/tests/views/kanban_mobile_tests.js
  56. +6 −1 addons/website/models/mixins.py
  57. +13 −0 addons/website/models/website.py
  58. +1 −1 addons/website/models/website_menu.py
  59. +52 −0 addons/website/tests/test_ui.py
  60. +11 −1 addons/website_event/static/src/js/website_geolocation.js
  61. +3 −0 addons/website_mass_mailing/views/res_config_settings_views.xml
  62. +1 −1 addons/website_payment/models/payment_acquirer.py
  63. +1 −1 addons/website_sale/models/product.py
  64. +6 −5 addons/website_sale_comparison/controllers/main.py
  65. +1 −1 addons/website_sale_wishlist/models/product_wishlist.py
  66. +1 −1 addons/website_twitter/models/website_twitter_tweet.py
  67. +11 −0 doc/cla/individual/igoris.md
  68. +11 −0 doc/cla/individual/reinaldoramosarxi.md
  69. +6 −2 doc/reference/qweb.rst
  70. +19 −0 odoo/__init__.py
  71. +1 −0 odoo/addons/base/data/res.lang.csv
  72. +3 −1 odoo/addons/base/models/ir_rule.py
  73. +1 −1 odoo/addons/base/models/res_users.py
  74. +2 −0 odoo/addons/base/tests/test_float.py
  75. +1 −1 odoo/addons/base/views/ir_module_views.xml
  76. +1 −0 odoo/addons/test_access_rights/ir.model.access.csv
  77. +12 −0 odoo/addons/test_access_rights/models.py
  78. +34 −4 odoo/addons/test_access_rights/tests/test_ir_rules.py
  79. +2 −1 odoo/tests/common.py
  80. +1 −1 odoo/tools/float_utils.py
@@ -2016,7 +2016,7 @@ def _onchange_account_id(self):
default_tax = self.invoice_id.company_id.account_sale_tax_id
else:
default_tax = self.invoice_id.company_id.account_purchase_tax_id
self.invoice_line_tax_ids = fpos.map_tax(self.account_id.tax_ids or default_tax, partner=self.partner_id).ids
self.invoice_line_tax_ids = fpos.map_tax(self.account_id.tax_ids or default_tax, partner=self.partner_id)
elif not self.price_unit:
self._set_taxes()

@@ -2089,7 +2089,7 @@ def create(self, vals_list):
@api.multi
def write(self, values):
if 'display_type' in values and self.filtered(lambda line: line.display_type != values.get('display_type')):
raise UserError("You cannot change the type of an invoice line. Instead you should delete the current line and create a new line of the proper type.")
raise UserError(_("You cannot change the type of an invoice line. Instead you should delete the current line and create a new line of the proper type."))
return super(AccountInvoiceLine, self).write(values)

_sql_constraints = [
@@ -418,16 +418,23 @@ def _check_lock_date(self):
def assert_balanced(self):
if not self.ids:
return True
prec = self.env.user.company_id.currency_id.decimal_places

self._cr.execute("""\
SELECT move_id
FROM account_move_line
WHERE move_id in %s
GROUP BY move_id
HAVING abs(sum(debit) - sum(credit)) > %s
""", (tuple(self.ids), 10 ** (-max(5, prec))))
if len(self._cr.fetchall()) != 0:

# /!\ As this method is called in create / write, we can't make the assumption the computed stored fields
# are already done. Then, this query MUST NOT depend of computed stored fields (e.g. balance).
# It happens as the ORM makes the create with the 'no_recompute' statement.
self._cr.execute('''
SELECT line.move_id
FROM account_move_line line
JOIN account_move move ON move.id = line.move_id
JOIN account_journal journal ON journal.id = move.journal_id
JOIN res_company company ON company.id = journal.company_id
JOIN res_currency currency ON currency.id = company.currency_id
WHERE line.move_id IN %s
GROUP BY line.move_id, currency.decimal_places
HAVING ROUND(SUM(debit - credit), currency.decimal_places) != 0.0;
''', [tuple(self.ids)])

if self._cr.fetchone():
raise UserError(_("Cannot create unbalanced journal entry."))
return True

@@ -674,9 +681,12 @@ def _compute_parent_state(self):
help="This field is used for payable and receivable journal entries. You can put the limit date for the payment of this line.")
date = fields.Date(related='move_id.date', string='Date', index=True, store=True, copy=False, readonly=False) # related is required
analytic_line_ids = fields.One2many('account.analytic.line', 'move_id', string='Analytic lines', oldname="analytic_lines")
tax_ids = fields.Many2many('account.tax', string='Taxes', domain=['|', ('active', '=', False), ('active', '=', True)])
tax_line_id = fields.Many2one('account.tax', string='Originator tax', ondelete='restrict', compute='_compute_tax_line_id', store=True)
tax_repartition_line_id = fields.Many2one(comodel_name='account.tax.repartition.line', string="Originator Tax Repartition Line", ondelete='restrict', help="Tax repartition line that caused the creation of this move line, if any")
tax_ids = fields.Many2many('account.tax', string='Taxes Applied', domain=['|', ('active', '=', False), ('active', '=', True)],
help="Taxes that apply on the base amount")
tax_line_id = fields.Many2one('account.tax', string='Originator tax', ondelete='restrict', compute='_compute_tax_line_id', store=True,
help="Indicates that this journal item is a tax line")
tax_repartition_line_id = fields.Many2one(comodel_name='account.tax.repartition.line', string="Originator Tax Repartition Line", ondelete='restrict',
help="Tax repartition line that caused the creation of this move line, if any")
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', index=True)
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
company_id = fields.Many2one('res.company', related='account_id.company_id', string='Company', store=True, readonly=True)
@@ -454,7 +454,10 @@ def cancel(self):
move.line_ids.remove_move_reconcile()
move.button_cancel()
move.unlink()
rec.state = 'cancelled'
rec.write({
'state': 'cancelled',
'move_name': False,
})

@api.multi
def unlink(self):
@@ -572,6 +572,8 @@ def _check_rule_propositions(self, statement_line, candidates):
'''
if not self.match_total_amount:
return True
if not candidates:
return False

# Match total residual amount.
total_residual = 0.0
@@ -331,7 +331,7 @@
<field name="module_account_sepa_direct_debit" class="oe_inline" widget="upgrade_boolean"/>
</div>
<div class="o_setting_right_pane" name="sepa_direct_debit_right_pane">
<label string="SEPA Direct Debit (SDD)" for="module_account_sepa"/>
<label string="SEPA Direct Debit (SDD)" for="module_account_sepa_direct_debit"/>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." aria-label="Values set here are company-specific." groups="base.group_multi_company" role="img"/>
<div class="text-muted">
Collect customer payments in one-click using Euro SEPA Service
@@ -163,6 +163,7 @@ def _check_iban(self):
'sm': 'SMkk KBBB BBSS SSSC CCCC CCCC CCC', # San Marino
'tn': 'TNkk BBSS SCCC CCCC CCCC CCCC', # Tunisia
'tr': 'TRkk BBBB BRCC CCCC CCCC CCCC CC', # Turkey
'ua': 'UAkk BBBB BBCC CCCC CCCC CCCC CCCC C', # Ukraine
'vg': 'VGkk BBBB CCCC CCCC CCCC CCCC', # Virgin Islands
'xk': 'XKkk BBBB CCCC CCCC CCCC', # Kosovo
}
@@ -446,6 +446,9 @@ def _try_match_date_time(self, preview_values, options):
# Or a date/datetime if it matches the pattern
date_patterns = [options['date_format']] if options.get(
'date_format') else []
user_date_format = self.env['res.lang']._lang_get(self.env.user.lang).date_format
if user_date_format:
date_patterns.append(user_date_format)
date_patterns.extend(DATE_PATTERNS)
match = check_patterns(date_patterns, preview_values)
if match:
@@ -14,6 +14,13 @@
"Install it to support more countries, for example with `easy_install vatnumber`.")
vatnumber = None

# Although stdnum is a dependency of vatnumber, the import of the latter is surrounded by a try/except
# if it is not installed. Therefore, we cannot be sure stdnum is installed in all cases.
try:
import stdnum
except ImportError:
stdnum = None

from odoo import api, models, _
from odoo.tools.misc import ustr
from odoo.exceptions import ValidationError
@@ -63,6 +70,8 @@
'be': 'BE0477472701',
'bg': 'BG1234567892',
'ch': 'CHE-123.456.788 TVA or CH TVA 123456', # Swiss by Yannick Vaucher @ Camptocamp
'cl': 'CL76086428-5',
'co': 'CO213123432-1 or CO213.123.432-1',
'cy': 'CY12345678F',
'cz': 'CZ12345679',
'de': 'DE123456788',
@@ -398,6 +407,12 @@ def check_vat_al(self, vat):
except ImportError:
return True

def check_vat_cl(self, vat):
return stdnum.util.get_cc_module('cl', 'vat').is_valid(vat) if stdnum else True

def check_vat_co(self, vat):
return stdnum.util.get_cc_module('co', 'vat').is_valid(vat) if stdnum else True

def default_compact(self, vat):
return vat

@@ -122,8 +122,8 @@
<field name="stop" attrs="{'invisible': True}"/>
<field name="id" attrs="{'invisible': True}"/>

<field name="start_date" string="Starting at" attrs="{'required': [('allday','=',True)], 'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
<field name="stop_date" string="Ending at" attrs="{'required': [('allday','=',True)],'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
<field name="start_date" string="Starting at" attrs="{'required': [('allday','=',True)], 'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}" force_save="1"/>
<field name="stop_date" string="Ending at" attrs="{'required': [('allday','=',True)],'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}" force_save="1"/>

<field name="start_datetime" string="Starting at" attrs="{'required': [('allday','=',False)], 'invisible': [('allday','=',True)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
<field name="stop_datetime" invisible="1"/>
@@ -132,7 +132,7 @@
<field name="duration" widget="float_time" string="Duration" class="oe_inline" attrs="{'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
<span> hours</span>
</div>
<field name="allday" attrs="{'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
<field name="allday" attrs="{'readonly': [('id', '!=', False), ('recurrency','=',True)]}" force_save="1"/>
</group>
<group>
<field name="categ_ids" widget="many2many_tags" options="
@@ -352,7 +352,7 @@
<div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_global_click">
<div class="o_dropdown_kanban dropdown">

<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown" data-display="static" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
@@ -317,7 +317,7 @@
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click">
<div class="o_dropdown_kanban dropdown">

<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown" data-display="static" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
@@ -194,7 +194,7 @@
<t t-name="kanban-box">
<div class="oe_kanban_card oe_kanban_global_click">
<div class="o_dropdown_kanban dropdown" groups="base.group_user">
<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown" data-display="static" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
@@ -893,24 +893,21 @@ def cut(self, mode=''):
self._raw(PAPER_FULL_CUT)


def cashdraw(self, pin, tries=5):
def cashdraw(self, pin):
""" Send pulse to kick the cash drawer
With some printers the drawer will not open after one pulse, for this reason we will check
the drawer status up to 'tries' times and send a new pulse if the drawer is still closed. If the
drawer status is open, we will stop sending pulses.
For some reason, with some printers (ex: Epson TM-m30), the cash drawer
only opens 50% of the time if you just send the pulse. But if you read
the status afterwards, it opens all the time.
"""
for i in range(tries):
if pin == 2:
self._raw(CD_KICK_2)
elif pin == 5:
self._raw(CD_KICK_5)
else:
raise CashDrawerError()

if i != tries - 1 and not self.get_printer_status()['printer']['drawer_pin_high']:
break
if pin == 2:
self._raw(CD_KICK_2)
elif pin == 5:
self._raw(CD_KICK_5)
else:
raise CashDrawerError()

self.get_printer_status()

def hw(self, hw):
""" Hardware operations """
@@ -7,7 +7,7 @@
import werkzeug.urls
import requests

from odoo import api, fields, models, exceptions
from odoo import api, fields, models, exceptions, _
from odoo.tools import pycompat

_logger = logging.getLogger(__name__)
@@ -68,7 +68,9 @@ def jsonrpc(url, method='call', params=None, timeout=15):
raise e
return response.get('result')
except (ValueError, requests.exceptions.ConnectionError, requests.exceptions.MissingSchema, requests.exceptions.Timeout, requests.exceptions.HTTPError) as e:
raise exceptions.AccessError('The url that this service requested returned an error. Please contact the author of the app. The url it tried to contact was ' + url)
raise exceptions.AccessError(
_('The url that this service requested returned an error. Please contact the author of the app. The url it tried to contact was %s') % url
)

#----------------------------------------------------------
# Helpers for proxy
@@ -828,6 +828,17 @@
<field name="tax_group_id" ref="ae_tax_group_5"/>
<field name="chart_template_id" ref="uae_chart_template_standard"/>
</record>
<record id="uae_purchase_tax_5_child_reverse" model="account.tax.template">
<field name="name">Reverse Charge TVA +5%(Purchase)</field>
<field name="type_tax_use">none</field>
<field name="amount">5</field>
<field name="amount_type">percent</field>
<field name="description">TVA 5%</field>
<field name="account_id" ref="uae_account_3726"/>
<field name="refund_account_id" ref="uae_account_3726"/>
<field name="tax_group_id" ref="ae_tax_group_5"/>
<field name="chart_template_id" ref="uae_chart_template_standard"/>
</record>
<record id="uae_purchase_tax_reverse_charge" model="account.tax.template">
<field name="name">Reverse Charge Provision</field>
<field name="type_tax_use">purchase</field>
@@ -95,7 +95,7 @@ def _check_before_xml_exporting(self):

# <1.2.1.8>
if not seller.l10n_it_tax_system:
raise UserError("The seller's company must have a tax system.")
raise UserError(_("The seller's company must have a tax system."))

# <1.2.2>
if not seller.street and not seller.street2:
@@ -110,7 +110,7 @@ def _check_before_xml_exporting(self):
raise UserError(_("%s must have a country.") % (seller.display_name))

# <1.4.1>
if not buyer.vat and not buyer.l10n_it_codice_fiscale:
if not buyer.vat and not buyer.l10n_it_codice_fiscale and buyer.country_id.code != 'IT':
raise UserError(_("The buyer, %s, or his company must have either a VAT number either a tax code (Codice Fiscale).") % (buyer.display_name))

# <1.4.2>
@@ -737,7 +737,7 @@ def _check_exoneration_with_no_tax(self):
for tax in self:
if tax.l10n_it_has_exoneration:
if not tax.l10n_it_kind_exoneration or not tax.l10n_it_law_reference or tax.amount != 0:
raise ValidationError("If the tax has exoneration, you must enter a kind of exoneration, a law reference and the amount of the tax must be 0.0.")
raise ValidationError(_("If the tax has exoneration, you must enter a kind of exoneration, a law reference and the amount of the tax must be 0.0."))
if tax.l10n_it_kind_exoneration == 'N6' and tax.l10n_it_vat_due_date == 'S':
raise UserError(_("'Scissione dei pagamenti' is not compatible with exoneration of kind 'N6'"))

@@ -35,7 +35,7 @@ class FetchmailServer(models.Model):
def _check_pec(self):
for record in self:
if record.l10n_it_is_pec and record.type != 'imap':
raise ValidationError("PEC mail server must be of type IMAP.")
raise ValidationError(_("PEC mail server must be of type IMAP."))

@api.multi
def fetch_mail(self):
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError

TAX_SYSTEM = [
@@ -94,7 +94,7 @@ def _check_eco_admin_index(self):
or not record.l10n_it_eco_index_share_capital\
or not record.l10n_it_eco_index_sole_shareholder\
or not record.l10n_it_eco_index_liquidation_state:
raise ValidationError("All fields about the Economic and Administrative Index must be completed.")
raise ValidationError(_("All fields about the Economic and Administrative Index must be completed."))

@api.constrains('l10n_it_has_tax_representative',
'l10n_it_tax_representative_partner_id')
@@ -103,8 +103,8 @@ def _check_tax_representative(self):
if not record.l10n_it_has_tax_representative:
continue
if not record.l10n_it_tax_representative_partner_id:
raise ValidationError("You must select a tax representative.")
raise ValidationError(_("You must select a tax representative."))
if not record.l10n_it_tax_representative_partner_id.vat:
raise ValidationError("Your tax representative partner must have a tax number.")
raise ValidationError(_("Your tax representative partner must have a tax number."))
if not record.l10n_it_tax_representative_partner_id.country_id:
raise ValidationError("Your tax representative partner must have a country.")
raise ValidationError(_("Your tax representative partner must have a country."))

0 comments on commit 168e54d

Please sign in to comment.
You can’t perform that action at this time.