[MERGE] Forward port of saas-11.3 to 12.0 up to 06f8f04699
closes odoo/odoo#35064 Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
This commit is contained in:
@@ -560,20 +560,25 @@ class account_payment(models.Model):
|
||||
|
||||
@api.multi
|
||||
def button_invoices(self):
|
||||
if self.partner_type == 'supplier':
|
||||
views = [(self.env.ref('account.invoice_supplier_tree').id, 'tree'), (self.env.ref('account.invoice_supplier_form').id, 'form')]
|
||||
else:
|
||||
views = [(self.env.ref('account.invoice_tree').id, 'tree'), (self.env.ref('account.invoice_form').id, 'form')]
|
||||
return {
|
||||
action = {
|
||||
'name': _('Paid Invoices'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'tree,form',
|
||||
'res_model': 'account.invoice',
|
||||
'view_id': False,
|
||||
'views': views,
|
||||
'type': 'ir.actions.act_window',
|
||||
'domain': [('id', 'in', [x.id for x in self.reconciled_invoice_ids])],
|
||||
}
|
||||
if self.partner_type == 'supplier':
|
||||
action['views'] = [(self.env.ref('account.invoice_supplier_tree').id, 'tree'), (self.env.ref('account.invoice_supplier_form').id, 'form')]
|
||||
action['context'] = {
|
||||
'journal_type': 'purchase',
|
||||
'type': 'in_invoice',
|
||||
'default_type': 'in_invoice',
|
||||
}
|
||||
else:
|
||||
action['views'] = [(self.env.ref('account.invoice_tree').id, 'tree'), (self.env.ref('account.invoice_form').id, 'form')]
|
||||
return action
|
||||
|
||||
@api.multi
|
||||
def button_dummy(self):
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<div t-if="pms or acquirers" id="payment_method" class="text-left col-md-13">
|
||||
<t t-call="payment.payment_tokens_list">
|
||||
<t t-set="mode" t-value="'payment'"/>
|
||||
<t t-set="partner_id" t-value="request.env.user.partner_id.id"/>
|
||||
<t t-set="partner_id" t-value="invoice.partner_id.id if request.env.user._is_public() else request.env.user.partner_id.id"/>
|
||||
<t t-set="success_url" t-value="invoice.get_portal_url()"/>
|
||||
<t t-set="error_url" t-value="invoice.get_portal_url()"/>
|
||||
<t t-set="access_token" t-value="access_token or ''"/>
|
||||
|
||||
@@ -27,6 +27,7 @@ invoices from picking, the system is able to add and compute the shipping line.
|
||||
'views/report_package_barcode.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'wizard/choose_delivery_package_views.xml',
|
||||
'views/assets.xml',
|
||||
],
|
||||
'demo': ['data/delivery_demo.xml'],
|
||||
'installable': True,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// TODO Remove me >=saas-12.5, this is a disgrace
|
||||
// This is an hack to get the string_* and hover_* options translated on the
|
||||
// boolean_button terminology widget. The terms are translated through
|
||||
// _t(opt_terms.string_true) which will only work if the terms are already
|
||||
// present in the translation map.
|
||||
// This code does nothing but marks the strings as translatable
|
||||
var _t = function(x) { return x; };
|
||||
|
||||
_t("Production Environment");
|
||||
_t("Switch to test environment");
|
||||
_t("Test Environment");
|
||||
_t("Switch to production environment");
|
||||
_t("Debug requests");
|
||||
_t("Disable debug");
|
||||
_t("No debug");
|
||||
_t("Enable debug");
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="assets_backend" name="delivery_assets_backend" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/delivery/static/src/js/delivery_translations.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -206,7 +206,7 @@
|
||||
<group>
|
||||
<field name="date" string="Invoice Date"/>
|
||||
<field name="start_date"/>
|
||||
<field name="expiration_date" />
|
||||
<field name="expiration_date" attrs="{'required': [('cost_frequency', '!=', 'no')]}"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="insurer_id" context="{'default_supplier': True}"/>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// TODO Remove me >=saas-12.5, this is a disgrace
|
||||
// This is an hack to get the string_true and hover_false options translated on
|
||||
// the boolean_button terminology widget. The terms are translated through
|
||||
// _t(opt_terms.string_true) which will only work if the terms are already
|
||||
// present in the translation map.
|
||||
// This code does nothing but marks the strings as translatable
|
||||
var _t = function(x) { return x; };
|
||||
|
||||
_t("Published");
|
||||
_t("Publish");
|
||||
@@ -13,4 +13,11 @@
|
||||
<script type="text/javascript" src="/payment/static/src/js/payment_processing.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_backend" name="payment_assets_backend" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/payment/static/src/js/payment_translations.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -25,6 +25,7 @@ class PaymentAcquirerAuthorize(models.Model):
|
||||
provider = fields.Selection(selection_add=[('authorize', 'Authorize.Net')])
|
||||
authorize_login = fields.Char(string='API Login Id', required_if_provider='authorize', groups='base.group_user')
|
||||
authorize_transaction_key = fields.Char(string='API Transaction Key', required_if_provider='authorize', groups='base.group_user')
|
||||
authorize_signature_key = fields.Char(string='API Signature Key', groups='base.group_user', compute="_compute_auth_signature_key", inverse="_inverse_auth_signature_key")
|
||||
|
||||
def _get_feature_support(self):
|
||||
"""Get advanced feature support by provider.
|
||||
@@ -42,6 +43,16 @@ class PaymentAcquirerAuthorize(models.Model):
|
||||
res['tokenize'].append('authorize')
|
||||
return res
|
||||
|
||||
def _compute_auth_signature_key(self):
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
for acquirer in self.filtered(lambda a: a.provider == 'authorize'):
|
||||
acquirer.authorize_signature_key = ICP.get_param('payment_authorize.signature_key_%s' % acquirer.id)
|
||||
|
||||
def _inverse_auth_signature_key(self):
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
for acquirer in self.filtered(lambda a: a.provider == 'authorize'):
|
||||
ICP.set_param('payment_authorize.signature_key_%s' % acquirer.id, acquirer.authorize_signature_key)
|
||||
|
||||
def _get_authorize_urls(self, environment):
|
||||
""" Authorize URLs """
|
||||
if environment == 'prod':
|
||||
@@ -57,13 +68,22 @@ class PaymentAcquirerAuthorize(models.Model):
|
||||
values['x_amount'],
|
||||
values['x_currency_code']]).encode('utf-8')
|
||||
|
||||
# [BACKWARD COMPATIBILITY] Check that the merchant did update his transaction
|
||||
# key to signature key (end of MD5 support from Authorize.net)
|
||||
# [BACKWARD COMPATIBILITY, 2nd edition]
|
||||
# The signature key is now '128-character hexadecimal format', while the
|
||||
# transaction key was only 16-character.
|
||||
if len(values['x_trans_key']) == 128:
|
||||
key = bytes.fromhex(values['x_trans_key'])
|
||||
return hmac.new(key, data, hashlib.sha512).hexdigest().upper()
|
||||
# One of 2 things should have happened:
|
||||
# 1/ the Transaction Key has been replaced with the Signature Key value (patch from March 2019)
|
||||
# => Use that to sign, but server-to-server won't work since it uses transaction key
|
||||
# as its credentials
|
||||
# 2/ the Signature key is a new field (patch from July 2019)
|
||||
# => Use that field for the signature
|
||||
|
||||
# FORWARD-PORT NOTE: forward part to saas-12.4 but no further
|
||||
if len(values['x_trans_key']) == 128 and not self.authorize_signature_key:
|
||||
self.authorize_signature_key = values['x_trans_key'] # store in the correct field
|
||||
return hmac.new(bytes.fromhex(values['x_trans_key']), data, hashlib.sha512).hexdigest().upper()
|
||||
elif self.authorize_signature_key:
|
||||
return hmac.new(bytes.fromhex(self.authorize_signature_key), data, hashlib.sha512).hexdigest().upper()
|
||||
else:
|
||||
return hmac.new(values['x_trans_key'].encode('utf-8'), data, hashlib.md5).hexdigest()
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<group attrs="{'invisible': [('provider', '!=', 'authorize')]}">
|
||||
<field name="authorize_login"/>
|
||||
<field name="authorize_transaction_key" password="True"/>
|
||||
<field name="authorize_signature_key" password="True"/>
|
||||
<a colspan="2" href="https://www.odoo.com/documentation/user/online/ecommerce/shopper_experience/authorize.html" target="_blank">How to get paid with Authorize.Net</a>
|
||||
</group>
|
||||
</xpath>
|
||||
|
||||
@@ -162,9 +162,23 @@ class Product(models.Model):
|
||||
company_id = self.env.user.company_id.id
|
||||
return (
|
||||
[('location_id.company_id', '=', company_id), ('location_id.usage', 'in', ['internal', 'transit'])],
|
||||
[('location_id.company_id', '=', False), ('location_dest_id.company_id', '=', company_id)],
|
||||
[('location_id.company_id', '=', company_id), ('location_dest_id.company_id', '=', False),
|
||||
])
|
||||
['&',
|
||||
('location_dest_id.company_id', '=', company_id),
|
||||
'|',
|
||||
('location_id.company_id', '=', False),
|
||||
'&',
|
||||
('location_id.usage', 'in', ['inventory', 'production']),
|
||||
('location_id.company_id', '=', company_id),
|
||||
],
|
||||
['&',
|
||||
('location_id.company_id', '=', company_id),
|
||||
'|',
|
||||
('location_dest_id.company_id', '=', False),
|
||||
'&',
|
||||
('location_dest_id.usage', 'in', ['inventory', 'production']),
|
||||
('location_dest_id.company_id', '=', company_id),
|
||||
]
|
||||
)
|
||||
location_ids = []
|
||||
if self.env.context.get('location', False):
|
||||
if isinstance(self.env.context['location'], pycompat.integer_types):
|
||||
|
||||
@@ -201,7 +201,7 @@ class StockMove(models.Model):
|
||||
'|',
|
||||
('location_dest_id.company_id', '=', False),
|
||||
'&',
|
||||
('location_dest_id.usage', '=', 'inventory'),
|
||||
('location_dest_id.usage', 'in', ['inventory', 'production']),
|
||||
('location_dest_id.company_id', '=', company_id or self.env.user.company_id.id),
|
||||
]
|
||||
return domain
|
||||
|
||||
@@ -3944,3 +3944,51 @@ class TestStockValuation(TransactionCase):
|
||||
|
||||
self.assertAlmostEqual(move1.remaining_qty, 0.0)
|
||||
self.assertAlmostEqual(self.product1.stock_value, 0.0)
|
||||
|
||||
def test_at_date_average_1(self):
|
||||
""" Set a company on the inventory loss, take items from there then put items there, check
|
||||
the values and quantities at date.
|
||||
"""
|
||||
now = Datetime.from_string(Date.today())
|
||||
date1 = now - timedelta(days=8)
|
||||
date2 = now - timedelta(days=7)
|
||||
|
||||
self.product1.standard_price = 10
|
||||
self.env['product.price.history'].search([
|
||||
('product_id', '=', self.product1.id)
|
||||
], order='datetime desc, id DESC', limit=1).datetime = date1
|
||||
self.product1.product_tmpl_id.cost_method = 'average'
|
||||
self.inventory_location.company_id = self.env.user.company_id.id
|
||||
|
||||
move1 = self.env['stock.move'].create({
|
||||
'name': 'Adjustment of 10 units',
|
||||
'location_id': self.inventory_location.id,
|
||||
'location_dest_id': self.stock_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 10.0,
|
||||
})
|
||||
move1._action_confirm()
|
||||
move1._action_assign()
|
||||
move1.move_line_ids.qty_done = 10.0
|
||||
move1._action_done()
|
||||
move1.date = date1
|
||||
|
||||
move2 = self.env['stock.move'].create({
|
||||
'name': 'Sell 5 units',
|
||||
'location_id': self.stock_location.id,
|
||||
'location_dest_id': self.inventory_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 5.0,
|
||||
})
|
||||
move2._action_confirm()
|
||||
move2._action_assign()
|
||||
move2.move_line_ids.qty_done = 5.0
|
||||
move2._action_done()
|
||||
move2.date = date2
|
||||
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date1)).qty_at_date, 10)
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date1)).stock_value, 100)
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date2)).qty_at_date, 5)
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date2)).stock_value, 50)
|
||||
|
||||
@@ -346,6 +346,7 @@
|
||||
<template id="products_attributes" inherit_id="website_sale.products" active="False" customize_show="True" name="Product Attribute's Filters">
|
||||
<xpath expr="//div[@id='products_grid_before']" position="inside">
|
||||
<form class="js_attributes" method="get">
|
||||
<input t-if="category" type="hidden" name="category" t-att-value="category.id" />
|
||||
<input type="hidden" name="search" t-att-value="search" />
|
||||
<ul class="nav nav-pills flex-column mt16">
|
||||
<t t-foreach="attributes" t-as="a">
|
||||
|
||||
@@ -490,6 +490,11 @@ on the extended template to select *context nodes* to which the specified
|
||||
the node's body replaces the context node's children
|
||||
``replace``
|
||||
the node's body is used to replace the context node itself
|
||||
``attributes``
|
||||
the nodes's body should be any number of ``attribute`` elements,
|
||||
each with a ``name`` attribute and some textual content, the named
|
||||
attribute of the context node will be set to the specified value
|
||||
(either replaced if it already existed or added if not)
|
||||
No operation
|
||||
if no ``t-operation`` is specified, the template body is interpreted as
|
||||
javascript code and executed with the context node as ``this``
|
||||
|
||||
Reference in New Issue
Block a user