Merge branch 'master-sheetal-06072018' into 'master-patch-july-2018'
Master sheetal 06072018 See merge request flectra-hq/flectra!96
This commit is contained in:
commit
3881ab378e
@ -78,6 +78,8 @@ class AuthSignupHome(Home):
|
|||||||
login, request.env.user.login, request.httprequest.remote_addr)
|
login, request.env.user.login, request.httprequest.remote_addr)
|
||||||
request.env['res.users'].sudo().reset_password(login)
|
request.env['res.users'].sudo().reset_password(login)
|
||||||
qcontext['message'] = _("An email has been sent with credentials to reset your password")
|
qcontext['message'] = _("An email has been sent with credentials to reset your password")
|
||||||
|
except UserError as e:
|
||||||
|
qcontext['error'] = e.name or e.value
|
||||||
except SignupError:
|
except SignupError:
|
||||||
qcontext['error'] = _("Could not reset your password")
|
qcontext['error'] = _("Could not reset your password")
|
||||||
_logger.exception('error when resetting password')
|
_logger.exception('error when resetting password')
|
||||||
|
@ -45,6 +45,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="padding:0px;width:600px;margin:auto; margin-top: 10px; background: #fff repeat top /100%;color:#777777">
|
<div style="padding:0px;width:600px;margin:auto; margin-top: 10px; background: #fff repeat top /100%;color:#777777">
|
||||||
${user.signature | safe}
|
${user.signature | safe}
|
||||||
|
<p style="font-size: 11px; margin-top: 10px;">
|
||||||
|
<strong>Sent by ${user.company_id.name} using <a href="www.flectrahq.com" style="text-decoration:none; color: #875A7B;">Flectra</a></strong>
|
||||||
|
</p>
|
||||||
</div>]]></field>
|
</div>]]></field>
|
||||||
<field name="user_signature" eval="False"/>
|
<field name="user_signature" eval="False"/>
|
||||||
</record>
|
</record>
|
||||||
@ -81,6 +84,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="padding:0px;width:600px;margin:auto; margin-top: 10px; background: #fff repeat top /100%;color:#777777">
|
<div style="padding:0px;width:600px;margin:auto; margin-top: 10px; background: #fff repeat top /100%;color:#777777">
|
||||||
${user.signature | safe}
|
${user.signature | safe}
|
||||||
|
<p style="font-size: 11px; margin-top: 10px;">
|
||||||
|
<strong>Sent by ${user.company_id.name} using <a href="www.flectrahq.com" style="text-decoration:none; color: #875A7B;">Flectra</a></strong>
|
||||||
|
</p>
|
||||||
</div>]]></field>
|
</div>]]></field>
|
||||||
<field name="user_signature" eval="False"/>
|
<field name="user_signature" eval="False"/>
|
||||||
</record>
|
</record>
|
||||||
@ -122,6 +128,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="padding:0px;width:600px;margin:auto; margin-top: 10px; background: #fff repeat top /100%;color:#777777">
|
<div style="padding:0px;width:600px;margin:auto; margin-top: 10px; background: #fff repeat top /100%;color:#777777">
|
||||||
${user.signature | safe}
|
${user.signature | safe}
|
||||||
|
<p style="font-size: 11px; margin-top: 10px;">
|
||||||
|
<strong>Sent by ${user.company_id.name} using <a href="www.flectrahq.com" style="text-decoration:none; color: #875A7B;">Flectra</a></strong>
|
||||||
|
</p>
|
||||||
</div></field>
|
</div></field>
|
||||||
<field name="user_signature" eval="False"/>
|
<field name="user_signature" eval="False"/>
|
||||||
</record>
|
</record>
|
||||||
|
@ -32,6 +32,7 @@ class ResPartner(models.Model):
|
|||||||
signup_url = fields.Char(compute='_compute_signup_url', string='Signup URL')
|
signup_url = fields.Char(compute='_compute_signup_url', string='Signup URL')
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
|
@api.depends('signup_token', 'signup_expiration')
|
||||||
def _compute_signup_valid(self):
|
def _compute_signup_valid(self):
|
||||||
dt = now()
|
dt = now()
|
||||||
for partner in self:
|
for partner in self:
|
||||||
|
@ -155,5 +155,5 @@ class ResUsers(models.Model):
|
|||||||
sup = super(ResUsers, self)
|
sup = super(ResUsers, self)
|
||||||
if not default or not default.get('email'):
|
if not default or not default.get('email'):
|
||||||
# avoid sending email to the user we are duplicating
|
# avoid sending email to the user we are duplicating
|
||||||
sup = super(ResUsers, self.with_context(reset_password=False))
|
sup = super(ResUsers, self.with_context(no_reset_password=True))
|
||||||
return sup.copy(default=default)
|
return sup.copy(default=default)
|
||||||
|
@ -53,7 +53,7 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
|
|||||||
navigator.userAgent.match(/iPod/i) ||
|
navigator.userAgent.match(/iPod/i) ||
|
||||||
navigator.userAgent.match(/BlackBerry/i) ||
|
navigator.userAgent.match(/BlackBerry/i) ||
|
||||||
navigator.userAgent.match(/Windows Phone/i);
|
navigator.userAgent.match(/Windows Phone/i);
|
||||||
this.isChromeMobile = isMobile && window.chrome;
|
this.isChromeMobile = isMobile && navigator.userAgent.match(/Chrome/i);
|
||||||
|
|
||||||
// Creates an input who will receive the barcode scanner value.
|
// Creates an input who will receive the barcode scanner value.
|
||||||
if (this.isChromeMobile) {
|
if (this.isChromeMobile) {
|
||||||
@ -61,13 +61,17 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
|
|||||||
name: 'barcode',
|
name: 'barcode',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
css: {
|
css: {
|
||||||
'position': 'absolute',
|
'position': 'fixed',
|
||||||
'opacity': 0,
|
'top': '50%',
|
||||||
|
'transform': 'translateY(-50%)',
|
||||||
|
'z-index': '-1',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
// Avoid to show autocomplete for a non appearing input
|
||||||
|
this.$barcodeInput.attr('autocomplete', 'off');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.__removeBarcodeField = _.debounce(this._removeBarcodeField, this.inputTimeOut);
|
this.__blurBarcodeInput = _.debounce(this._blurBarcodeInput, this.inputTimeOut);
|
||||||
},
|
},
|
||||||
|
|
||||||
handle_buffered_keys: function() {
|
handle_buffered_keys: function() {
|
||||||
@ -230,7 +234,7 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
|
|||||||
this.max_time_between_keys_in_ms);
|
this.max_time_between_keys_in_ms);
|
||||||
}
|
}
|
||||||
// if the barcode input doesn't receive keydown for a while, remove it.
|
// if the barcode input doesn't receive keydown for a while, remove it.
|
||||||
this.__removeBarcodeField();
|
this.__blurBarcodeInput();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -245,21 +249,22 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
|
|||||||
var barcodeValue = this.$barcodeInput.val();
|
var barcodeValue = this.$barcodeInput.val();
|
||||||
if (barcodeValue.match(this.regexp)) {
|
if (barcodeValue.match(this.regexp)) {
|
||||||
core.bus.trigger('barcode_scanned', barcodeValue, $(e.target).parent()[0]);
|
core.bus.trigger('barcode_scanned', barcodeValue, $(e.target).parent()[0]);
|
||||||
this.$barcodeInput.val('');
|
this._blurBarcodeInput();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the temporary input created to store the barcode value.
|
* Removes the value and focus from the barcode input.
|
||||||
* If nothing happens, this input will be removed, so the focus will be lost
|
* If nothing happens, the focus will be lost and
|
||||||
* and the virtual keyboard on mobile devices will be closed.
|
* the virtual keyboard on mobile devices will be closed.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_removeBarcodeField: function () {
|
_blurBarcodeInput: function () {
|
||||||
if (this.$barcodeInput) {
|
if (this.$barcodeInput) {
|
||||||
// Reset the value and remove from the DOM.
|
// Close the virtual keyboard on mobile browsers
|
||||||
this.$barcodeInput.val('').remove();
|
// FIXME: actually we can't prevent keyboard from opening
|
||||||
|
this.$barcodeInput.val('').blur();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -146,10 +146,10 @@ FormController.include({
|
|||||||
* @param {Object} activeBarcode: options sent by the field who use barcode features
|
* @param {Object} activeBarcode: options sent by the field who use barcode features
|
||||||
* @returns {Deferred}
|
* @returns {Deferred}
|
||||||
*/
|
*/
|
||||||
_barcodeSelectedCandidate: function (candidate, record, barcode, activeBarcode) {
|
_barcodeSelectedCandidate: function (candidate, record, barcode, activeBarcode, quantity) {
|
||||||
var changes = {};
|
var changes = {};
|
||||||
var candidateChanges = {};
|
var candidateChanges = {};
|
||||||
candidateChanges[activeBarcode.quantity] = candidate.data[activeBarcode.quantity] + 1;
|
candidateChanges[activeBarcode.quantity] = quantity ? quantity : candidate.data[activeBarcode.quantity] + 1;
|
||||||
changes[activeBarcode.fieldName] = {
|
changes[activeBarcode.fieldName] = {
|
||||||
operation: 'UPDATE',
|
operation: 'UPDATE',
|
||||||
id: candidate.id,
|
id: candidate.id,
|
||||||
@ -283,6 +283,9 @@ FormController.include({
|
|||||||
function (reserved) {return barcode.indexOf(reserved) === 0;});
|
function (reserved) {return barcode.indexOf(reserved) === 0;});
|
||||||
var hasCommand = false;
|
var hasCommand = false;
|
||||||
var defs = [];
|
var defs = [];
|
||||||
|
if (! $.contains(target, self.el)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (var k in self.activeBarcode) {
|
for (var k in self.activeBarcode) {
|
||||||
var activeBarcode = self.activeBarcode[k];
|
var activeBarcode = self.activeBarcode[k];
|
||||||
// Handle the case where there are several barcode widgets on the same page. Since the
|
// Handle the case where there are several barcode widgets on the same page. Since the
|
||||||
@ -306,6 +309,8 @@ FormController.include({
|
|||||||
}
|
}
|
||||||
return self.alive($.when.apply($, defs)).then(function () {
|
return self.alive($.when.apply($, defs)).then(function () {
|
||||||
if (!prefixed) {
|
if (!prefixed) {
|
||||||
|
// remember the barcode scanned for the quantity listener
|
||||||
|
self.current_barcode = barcode;
|
||||||
// redraw the view if we scanned a real barcode (required if
|
// redraw the view if we scanned a real barcode (required if
|
||||||
// we manually apply the change in JS, e.g. incrementing the
|
// we manually apply the change in JS, e.g. incrementing the
|
||||||
// quantity)
|
// quantity)
|
||||||
@ -321,6 +326,9 @@ FormController.include({
|
|||||||
_quantityListener: function (event) {
|
_quantityListener: function (event) {
|
||||||
var character = String.fromCharCode(event.which);
|
var character = String.fromCharCode(event.which);
|
||||||
|
|
||||||
|
if (! $.contains(event.target, this.el)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// only catch the event if we're not focused in
|
// only catch the event if we're not focused in
|
||||||
// another field and it's a number
|
// another field and it's a number
|
||||||
if (!$(event.target).is('body, .modal') || !/[0-9]/.test(character)) {
|
if (!$(event.target).is('body, .modal') || !/[0-9]/.test(character)) {
|
||||||
@ -355,9 +363,10 @@ FormController.include({
|
|||||||
title: _t('Set quantity'),
|
title: _t('Set quantity'),
|
||||||
buttons: [{text: _t('Select'), classes: 'btn-primary', close: true, click: function () {
|
buttons: [{text: _t('Select'), classes: 'btn-primary', close: true, click: function () {
|
||||||
var new_qty = this.$content.find('.o_set_qty_input').val();
|
var new_qty = this.$content.find('.o_set_qty_input').val();
|
||||||
var values = {};
|
var record = self.model.get(self.handle);
|
||||||
values[activeBarcode.quantity] = parseFloat(new_qty);
|
return self._barcodeSelectedCandidate(activeBarcode.candidate, record,
|
||||||
return self.model.notifyChanges(activeBarcode.candidate.id, values).then(function () {
|
self.current_barcode, activeBarcode, parseFloat(new_qty))
|
||||||
|
.then(function () {
|
||||||
self.update({}, {reload: false});
|
self.update({}, {reload: false});
|
||||||
});
|
});
|
||||||
}}, {text: _t('Discard'), close: true}],
|
}}, {text: _t('Discard'), close: true}],
|
||||||
|
@ -18,6 +18,9 @@ var BarcodeParser = Class.extend({
|
|||||||
// only when those data have been loaded
|
// only when those data have been loaded
|
||||||
load: function(){
|
load: function(){
|
||||||
var self = this;
|
var self = this;
|
||||||
|
if (!this.nomenclature_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var id = this.nomenclature_id[0];
|
var id = this.nomenclature_id[0];
|
||||||
rpc.query({
|
rpc.query({
|
||||||
model: 'barcode.nomenclature',
|
model: 'barcode.nomenclature',
|
||||||
|
@ -404,4 +404,136 @@ QUnit.test('specification of widget barcode_handler', function (assert) {
|
|||||||
barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms = delay;
|
barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms = delay;
|
||||||
delete fieldRegistry.map.test_barcode_handler;
|
delete fieldRegistry.map.test_barcode_handler;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test('specification of widget barcode_handler with keypress and notifyChange', function (assert) {
|
||||||
|
assert.expect(6);
|
||||||
|
var done = assert.async();
|
||||||
|
|
||||||
|
var delay = barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms;
|
||||||
|
barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms = 0;
|
||||||
|
|
||||||
|
this.data.order.onchanges = {
|
||||||
|
_barcode_scanned: function () {},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define a specific barcode_handler widget for this test case
|
||||||
|
var TestBarcodeHandler = AbstractField.extend({
|
||||||
|
init: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
|
||||||
|
this.trigger_up('activeBarcode', {
|
||||||
|
name: 'test',
|
||||||
|
fieldName: 'line_ids',
|
||||||
|
notifyChange: false,
|
||||||
|
setQuantityWithKeypress: true,
|
||||||
|
quantity: 'quantity',
|
||||||
|
commands: {
|
||||||
|
barcode: '_barcodeAddX2MQuantity',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
fieldRegistry.add('test_barcode_handler', TestBarcodeHandler);
|
||||||
|
|
||||||
|
var form = createView({
|
||||||
|
View: FormView,
|
||||||
|
model: 'order',
|
||||||
|
data: this.data,
|
||||||
|
arch: '<form>' +
|
||||||
|
'<field name="_barcode_scanned" widget="test_barcode_handler"/>' +
|
||||||
|
'<field name="line_ids">' +
|
||||||
|
'<tree>' +
|
||||||
|
'<field name="product_id"/>' +
|
||||||
|
'<field name="product_barcode" invisible="1"/>' +
|
||||||
|
'<field name="quantity"/>' +
|
||||||
|
'</tree>' +
|
||||||
|
'</field>' +
|
||||||
|
'</form>',
|
||||||
|
mockRPC: function (route, args) {
|
||||||
|
assert.step(args.method);
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
|
res_id: 1,
|
||||||
|
viewOptions: {
|
||||||
|
mode: 'edit',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
_.each(['1','2','3','4','5','6','7','8','9','0','Enter'], triggerKeypressEvent);
|
||||||
|
// Quantity listener should open a dialog.
|
||||||
|
triggerKeypressEvent('5');
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
var keycode = $.ui.keyCode.ENTER;
|
||||||
|
|
||||||
|
assert.strictEqual($('.modal .modal-body').length, 1, 'should open a modal with a quantity as input');
|
||||||
|
assert.strictEqual($('.modal .modal-body .o_set_qty_input').val(), '5', 'the quantity by default in the modal shoud be 5');
|
||||||
|
|
||||||
|
$('.modal .modal-body .o_set_qty_input').val('7');
|
||||||
|
|
||||||
|
$('.modal .modal-body .o_set_qty_input').trigger($.Event('keypress', {which: keycode, keyCode: keycode}));
|
||||||
|
assert.strictEqual(form.$('.o_data_row .o_data_cell:nth(1)').text(), '7',
|
||||||
|
"quantity checked should be 7");
|
||||||
|
|
||||||
|
assert.verifySteps(['read', 'read']);
|
||||||
|
|
||||||
|
form.destroy();
|
||||||
|
barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms = delay;
|
||||||
|
delete fieldRegistry.map.test_barcode_handler;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
QUnit.test('barcode_scanned only trigger error for active view', function (assert) {
|
||||||
|
assert.expect(2);
|
||||||
|
|
||||||
|
this.data.order_line.fields._barcode_scanned = {string: 'Barcode scanned', type: 'char'};
|
||||||
|
|
||||||
|
var form = createView({
|
||||||
|
View: FormView,
|
||||||
|
model: 'order',
|
||||||
|
data: this.data,
|
||||||
|
arch: '<form>' +
|
||||||
|
'<field name="_barcode_scanned" widget="barcode_handler"/>' +
|
||||||
|
'<field name="line_ids">' +
|
||||||
|
'<tree>' +
|
||||||
|
'<field name="product_id"/>' +
|
||||||
|
'<field name="product_barcode" invisible="1"/>' +
|
||||||
|
'<field name="quantity"/>' +
|
||||||
|
'</tree>' +
|
||||||
|
'</field>' +
|
||||||
|
'</form>',
|
||||||
|
archs: {
|
||||||
|
"order_line,false,form":
|
||||||
|
'<form string="order line">' +
|
||||||
|
'<field name="_barcode_scanned" widget="barcode_handler"/>' +
|
||||||
|
'<field name="product_id"/>' +
|
||||||
|
'</form>',
|
||||||
|
},
|
||||||
|
res_id: 1,
|
||||||
|
intercepts: {
|
||||||
|
warning: function (event) {
|
||||||
|
assert.step(event.name);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
viewOptions: {
|
||||||
|
mode: 'edit',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
form.$('.o_data_row:first').click();
|
||||||
|
|
||||||
|
// We do not trigger on the body since modal and
|
||||||
|
// form view are both inside it.
|
||||||
|
function modalTriggerKeypressEvent(char) {
|
||||||
|
var keycode;
|
||||||
|
if (char === "Enter") {
|
||||||
|
keycode = $.ui.keyCode.ENTER;
|
||||||
|
} else {
|
||||||
|
keycode = char.charCodeAt(0);
|
||||||
|
}
|
||||||
|
return $('.modal').trigger($.Event('keypress', {which: keycode, keyCode: keycode}));
|
||||||
|
}
|
||||||
|
_.each(['O','-','B','T','N','.','c','a','n','c','e','l','Enter'], modalTriggerKeypressEvent);
|
||||||
|
assert.verifySteps(['warning'], "only one event should be triggered");
|
||||||
|
form.destroy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@ from flectra.tools.translate import _
|
|||||||
class Partner(models.Model):
|
class Partner(models.Model):
|
||||||
_inherit = 'res.partner'
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
country_enforce_cities = fields.Boolean(related='country_id.enforce_cities')
|
country_enforce_cities = fields.Boolean(related='country_id.enforce_cities', readonly=True)
|
||||||
city_id = fields.Many2one('res.city', string='City')
|
city_id = fields.Many2one('res.city', string='City')
|
||||||
|
|
||||||
@api.onchange('city_id')
|
@api.onchange('city_id')
|
||||||
|
@ -17,7 +17,8 @@ For example, in legal reports, some countries need to split the street into seve
|
|||||||
with the street name, the house number, and room number.
|
with the street name, the house number, and room number.
|
||||||
""",
|
""",
|
||||||
'data': [
|
'data': [
|
||||||
'views/base_address_extended.xml'
|
'views/base_address_extended.xml',
|
||||||
|
'data/base_address_extended_data.xml',
|
||||||
],
|
],
|
||||||
'depends': ['base'],
|
'depends': ['base'],
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<flectra>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="base.nl" model="res.country">
|
||||||
|
<field eval="'%(street_name)s %(street_number)s/%(street_number2)s'" name="street_format" />
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</flectra>
|
@ -89,6 +89,7 @@ class WebsitePayment(http.Controller):
|
|||||||
'amount': float(amount),
|
'amount': float(amount),
|
||||||
'currency_id': currency_id,
|
'currency_id': currency_id,
|
||||||
'partner_id': partner_id,
|
'partner_id': partner_id,
|
||||||
|
'type': 'form_save' if acquirer.save_token != 'none' and partner_id else 'form',
|
||||||
}
|
}
|
||||||
|
|
||||||
tx = request.env['payment.transaction'].sudo().create(values)
|
tx = request.env['payment.transaction'].sudo().create(values)
|
||||||
@ -117,7 +118,8 @@ class WebsitePayment(http.Controller):
|
|||||||
'amount': float(amount),
|
'amount': float(amount),
|
||||||
'currency_id': int(currency_id),
|
'currency_id': int(currency_id),
|
||||||
'partner_id': partner_id,
|
'partner_id': partner_id,
|
||||||
'payment_token_id': pm_id
|
'payment_token_id': pm_id,
|
||||||
|
'type': 'form_save' if token.acquirer_id.save_token != 'none' and partner_id else 'form',
|
||||||
}
|
}
|
||||||
|
|
||||||
tx = request.env['payment.transaction'].sudo().create(values)
|
tx = request.env['payment.transaction'].sudo().create(values)
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
|
|
||||||
<template id="default_acquirer_button">
|
<template id="default_acquirer_button">
|
||||||
<input type="hidden" name="data_set" t-att-data-action-url="tx_url"/>
|
<input type="hidden" name="data_set" t-att-data-action-url="tx_url"/>
|
||||||
<input type="hidden" name="csrf_token"
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||||
t-att-value="request.csrf_token()"/>
|
|
||||||
<t t-if="return_url">
|
<t t-if="return_url">
|
||||||
<input type="hidden" name="return_url" t-att-value="return_url"/>
|
<input type="hidden" name="return_url" t-att-value="return_url"/>
|
||||||
</t>
|
</t>
|
||||||
@ -148,8 +147,7 @@
|
|||||||
</field>
|
</field>
|
||||||
<field name="description" type="html">
|
<field name="description" type="html">
|
||||||
<p>
|
<p>
|
||||||
Provide instructions to customers so that they can pay their
|
Provide instructions to customers so that they can pay their orders manually.
|
||||||
orders manually.
|
|
||||||
</p>
|
</p>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
from . import payment_acquirer
|
from . import payment_acquirer
|
||||||
from . import res_partner
|
from . import res_partner
|
||||||
from . import account_payment
|
from . import account_payment
|
||||||
|
from . import chart_template
|
||||||
|
14
addons/payment/models/chart_template.py
Normal file
14
addons/payment/models/chart_template.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from flectra import api, fields, models, _
|
||||||
|
|
||||||
|
|
||||||
|
class WizardMultiChartsAccounts(models.TransientModel):
|
||||||
|
_inherit = 'wizard.multi.charts.accounts'
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _create_bank_journals_from_o2m(self, company, acc_template_ref):
|
||||||
|
res = super(WizardMultiChartsAccounts, self)._create_bank_journals_from_o2m(company, acc_template_ref)
|
||||||
|
|
||||||
|
# Try to generate the missing journals
|
||||||
|
return res + self.env['payment.acquirer']._create_missing_journal_for_acquirers(company=company)
|
@ -8,6 +8,7 @@ from flectra import api, exceptions, fields, models, _
|
|||||||
from flectra.tools import consteq, float_round, image_resize_images, image_resize_image, ustr
|
from flectra.tools import consteq, float_round, image_resize_images, image_resize_image, ustr
|
||||||
from flectra.addons.base.module import module
|
from flectra.addons.base.module import module
|
||||||
from flectra.exceptions import ValidationError
|
from flectra.exceptions import ValidationError
|
||||||
|
from flectra import api, SUPERUSER_ID
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -20,6 +21,11 @@ def _partner_split_name(partner_name):
|
|||||||
return [' '.join(partner_name.split()[:-1]), ' '.join(partner_name.split()[-1:])]
|
return [' '.join(partner_name.split()[:-1]), ' '.join(partner_name.split()[-1:])]
|
||||||
|
|
||||||
|
|
||||||
|
def create_missing_journal_for_acquirers(cr, registry):
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
env['payment.acquirer']._create_missing_journal_for_acquirers()
|
||||||
|
|
||||||
|
|
||||||
class PaymentAcquirer(models.Model):
|
class PaymentAcquirer(models.Model):
|
||||||
""" Acquirer Model. Each specific acquirer can extend the model by adding
|
""" Acquirer Model. Each specific acquirer can extend the model by adding
|
||||||
its own fields, using the acquirer_name as a prefix for the new fields.
|
its own fields, using the acquirer_name as a prefix for the new fields.
|
||||||
@ -51,6 +57,9 @@ class PaymentAcquirer(models.Model):
|
|||||||
_description = 'Payment Acquirer'
|
_description = 'Payment Acquirer'
|
||||||
_order = 'website_published desc, sequence, name'
|
_order = 'website_published desc, sequence, name'
|
||||||
|
|
||||||
|
def _get_default_view_template_id(self):
|
||||||
|
return self.env.ref('payment.default_acquirer_button', raise_if_not_found=False)
|
||||||
|
|
||||||
name = fields.Char('Name', required=True, translate=True)
|
name = fields.Char('Name', required=True, translate=True)
|
||||||
description = fields.Html('Description')
|
description = fields.Html('Description')
|
||||||
sequence = fields.Integer('Sequence', default=10, help="Determine the display order")
|
sequence = fields.Integer('Sequence', default=10, help="Determine the display order")
|
||||||
@ -196,6 +205,58 @@ class PaymentAcquirer(models.Model):
|
|||||||
"""
|
"""
|
||||||
return dict(authorize=[], tokenize=[], fees=[])
|
return dict(authorize=[], tokenize=[], fees=[])
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _prepare_account_journal_vals(self):
|
||||||
|
'''Prepare the values to create the acquirer's journal.
|
||||||
|
:return: a dictionary to create a account.journal record.
|
||||||
|
'''
|
||||||
|
self.ensure_one()
|
||||||
|
account_vals = self.env['account.journal']._prepare_liquidity_account(
|
||||||
|
self.name, self.company_id, None, 'bank')
|
||||||
|
account_vals['user_type_id'] = self.env.ref('account.data_account_type_current_assets').id
|
||||||
|
account_vals['reconcile'] = True
|
||||||
|
account = self.env['account.account'].create(account_vals)
|
||||||
|
return {
|
||||||
|
'name': self.name,
|
||||||
|
'code': self.name.upper(),
|
||||||
|
'sequence': 999,
|
||||||
|
'type': 'bank',
|
||||||
|
'company_id': self.company_id.id,
|
||||||
|
'default_debit_account_id': account.id,
|
||||||
|
'default_credit_account_id': account.id,
|
||||||
|
# Show the journal on dashboard if the acquirer is published on the website.
|
||||||
|
'show_on_dashboard': self.website_published,
|
||||||
|
# Don't show payment methods in the backend.
|
||||||
|
'inbound_payment_method_ids': [],
|
||||||
|
'outbound_payment_method_ids': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _create_missing_journal_for_acquirers(self, company=None):
|
||||||
|
'''Create the journal for active acquirers.
|
||||||
|
We want one journal per acquirer. However, we can't create them during the 'create' of the payment.acquirer
|
||||||
|
because every acquirers are defined on the 'payment' module but is active only when installing their own module
|
||||||
|
(e.g. payment_paypal for Paypal). We can't do that in such modules because we have no guarantee the chart template
|
||||||
|
is already installed.
|
||||||
|
'''
|
||||||
|
# Search for installed acquirers modules.
|
||||||
|
# If this method is triggered by a post_init_hook, the module is 'to install'.
|
||||||
|
# If the trigger comes from the chart template wizard, the modules are already installed.
|
||||||
|
acquirer_modules = self.env['ir.module.module'].search(
|
||||||
|
[('name', 'like', 'payment_%'), ('state', 'in', ('to install', 'installed'))])
|
||||||
|
acquirer_names = [a.name.split('_')[1] for a in acquirer_modules]
|
||||||
|
|
||||||
|
# Search for acquirers having no journal
|
||||||
|
company = company or self.env.user.company_id
|
||||||
|
acquirers = self.env['payment.acquirer'].search(
|
||||||
|
[('provider', 'in', acquirer_names), ('journal_id', '=', False), ('company_id', '=', company.id)])
|
||||||
|
|
||||||
|
journals = self.env['account.journal']
|
||||||
|
for acquirer in acquirers.filtered(lambda l: not l.journal_id and l.company_id.chart_template_id):
|
||||||
|
acquirer.journal_id = self.env['account.journal'].create(acquirer._prepare_account_journal_vals())
|
||||||
|
journals += acquirer.journal_id
|
||||||
|
return journals
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
image_resize_images(vals)
|
image_resize_images(vals)
|
||||||
@ -208,7 +269,13 @@ class PaymentAcquirer(models.Model):
|
|||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def toggle_website_published(self):
|
def toggle_website_published(self):
|
||||||
self.write({'website_published': not self.website_published})
|
''' When clicking on the website publish toggle button, the website_published is reversed and
|
||||||
|
the acquirer journal is set or not in favorite on the dashboard.
|
||||||
|
'''
|
||||||
|
self.ensure_one()
|
||||||
|
self.website_published = not self.website_published
|
||||||
|
if self.journal_id:
|
||||||
|
self.journal_id.show_on_dashboard = self.website_published
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@ -617,6 +684,10 @@ class PaymentTransaction(models.Model):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_next_reference(self, reference):
|
def get_next_reference(self, reference):
|
||||||
|
return self._get_next_reference(reference)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_next_reference(self, reference, acquirer=None):
|
||||||
ref_suffix = 1
|
ref_suffix = 1
|
||||||
init_ref = reference
|
init_ref = reference
|
||||||
while self.env['payment.transaction'].sudo().search_count([('reference', '=', reference)]):
|
while self.env['payment.transaction'].sudo().search_count([('reference', '=', reference)]):
|
||||||
|
@ -414,8 +414,14 @@ flectra.define('payment.payment_form', function (require) {
|
|||||||
},
|
},
|
||||||
displayError: function (title, message) {
|
displayError: function (title, message) {
|
||||||
var $checkedRadio = this.$('input[type="radio"]:checked'),
|
var $checkedRadio = this.$('input[type="radio"]:checked'),
|
||||||
acquirerID = this.getAcquirerIdFromRadio($checkedRadio[0]),
|
acquirerID = this.getAcquirerIdFromRadio($checkedRadio[0]);
|
||||||
|
var $acquirerForm;
|
||||||
|
if (this.isNewPaymentRadio($checkedRadio[0])) {
|
||||||
$acquirerForm = this.$('#o_payment_add_token_acq_' + acquirerID);
|
$acquirerForm = this.$('#o_payment_add_token_acq_' + acquirerID);
|
||||||
|
}
|
||||||
|
else if (this.isFormPaymentRadio($checkedRadio[0])) {
|
||||||
|
$acquirerForm = this.$('#o_payment_form_acq_' + acquirerID);
|
||||||
|
}
|
||||||
|
|
||||||
if ($checkedRadio.length === 0) {
|
if ($checkedRadio.length === 0) {
|
||||||
return new Dialog(null, {
|
return new Dialog(null, {
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
</page>
|
</page>
|
||||||
<page string="Messages">
|
<page string="Messages">
|
||||||
<group>
|
<group>
|
||||||
<field name="pre_msg"/>
|
<field name="pre_msg" invisible="1"/>
|
||||||
<field name="post_msg"/>
|
<field name="post_msg"/>
|
||||||
<field name="pending_msg"/>
|
<field name="pending_msg"/>
|
||||||
<field name="done_msg"/>
|
<field name="done_msg"/>
|
||||||
@ -81,7 +81,7 @@
|
|||||||
</page>
|
</page>
|
||||||
<page string="Configuration">
|
<page string="Configuration">
|
||||||
<group name="acquirer_config">
|
<group name="acquirer_config">
|
||||||
<field name="journal_id" context="{'default_type': 'bank'}" required="1"/>
|
<field name="journal_id" context="{'default_type': 'bank'}"/>
|
||||||
<field name="capture_manually" attrs="{'invisible': [('authorize_implemented', '=', False)]}"/>
|
<field name="capture_manually" attrs="{'invisible': [('authorize_implemented', '=', False)]}"/>
|
||||||
<field name="save_token" widget="radio" attrs="{'invisible': ['|', ('token_implemented', '=', False), ('payment_flow', '=', 's2s')]}"/>
|
<field name="save_token" widget="radio" attrs="{'invisible': ['|', ('token_implemented', '=', False), ('payment_flow', '=', 's2s')]}"/>
|
||||||
<field name="fees_active" attrs="{'invisible': [('fees_implemented', '=', False)]}"/>
|
<field name="fees_active" attrs="{'invisible': [('fees_implemented', '=', False)]}"/>
|
||||||
@ -113,7 +113,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<field name="registration_view_template_id" groups="base.group_no_one"/>
|
<field name="registration_view_template_id" groups="base.group_no_one"/>
|
||||||
<field name="payment_icon_ids" widget="many2many_tags"/>
|
<field name="payment_icon_ids" widget="many2many_tags"/>
|
||||||
<field name="payment_flow" widget="radio"/>
|
<field name="payment_flow" widget="radio" attrs="{'invisible': [('token_implemented', '=', False)]}"/>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
</notebook>
|
</notebook>
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from flectra.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
|
||||||
|
@ -14,4 +14,5 @@
|
|||||||
'data/payment_acquirer_data.xml',
|
'data/payment_acquirer_data.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
'post_init_hook': 'create_missing_journal_for_acquirers',
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ from werkzeug import urls
|
|||||||
from flectra import api, fields, models, tools, _
|
from flectra import api, fields, models, tools, _
|
||||||
from flectra.addons.payment.models.payment_acquirer import ValidationError
|
from flectra.addons.payment.models.payment_acquirer import ValidationError
|
||||||
from flectra.addons.payment_adyen.controllers.main import AdyenController
|
from flectra.addons.payment_adyen.controllers.main import AdyenController
|
||||||
|
from flectra.tools.pycompat import to_native
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -192,7 +193,7 @@ class TxAdyen(models.Model):
|
|||||||
shasign_check = tx.acquirer_id._adyen_generate_merchant_sig_sha256('out', data)
|
shasign_check = tx.acquirer_id._adyen_generate_merchant_sig_sha256('out', data)
|
||||||
else:
|
else:
|
||||||
shasign_check = tx.acquirer_id._adyen_generate_merchant_sig('out', data)
|
shasign_check = tx.acquirer_id._adyen_generate_merchant_sig('out', data)
|
||||||
if shasign_check != data.get('merchantSig'):
|
if to_native(shasign_check) != to_native(data.get('merchantSig')):
|
||||||
error_msg = _('Adyen: invalid merchantSig, received %s, computed %s') % (data.get('merchantSig'), shasign_check)
|
error_msg = _('Adyen: invalid merchantSig, received %s, computed %s') % (data.get('merchantSig'), shasign_check)
|
||||||
_logger.warning(error_msg)
|
_logger.warning(error_msg)
|
||||||
raise ValidationError(error_msg)
|
raise ValidationError(error_msg)
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from flectra.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
|
||||||
|
@ -14,4 +14,5 @@
|
|||||||
'data/payment_acquirer_data.xml',
|
'data/payment_acquirer_data.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
'post_init_hook': 'create_missing_journal_for_acquirers',
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import time
|
|||||||
from flectra import _, api, fields, models
|
from flectra import _, api, fields, models
|
||||||
from flectra.addons.payment.models.payment_acquirer import ValidationError
|
from flectra.addons.payment.models.payment_acquirer import ValidationError
|
||||||
from flectra.addons.payment_authorize.controllers.main import AuthorizeController
|
from flectra.addons.payment_authorize.controllers.main import AuthorizeController
|
||||||
from flectra.tools.float_utils import float_compare
|
from flectra.tools.float_utils import float_compare, float_repr
|
||||||
from flectra.tools.safe_eval import safe_eval
|
from flectra.tools.safe_eval import safe_eval
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
@ -59,12 +59,21 @@ class PaymentAcquirerAuthorize(models.Model):
|
|||||||
@api.multi
|
@api.multi
|
||||||
def authorize_form_generate_values(self, values):
|
def authorize_form_generate_values(self, values):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
# State code is only supported in US, use state name by default
|
||||||
|
# See https://developer.authorize.net/api/reference/
|
||||||
|
state = values['partner_state'].name if values.get('partner_state') else ''
|
||||||
|
if values.get('partner_country') and values.get('partner_country') == self.env.ref('base.us', False):
|
||||||
|
state = values['partner_state'].code if values.get('partner_state') else ''
|
||||||
|
billing_state = values['billing_partner_state'].name if values.get('billing_partner_state') else ''
|
||||||
|
if values.get('billing_partner_country') and values.get('billing_partner_country') == self.env.ref('base.us', False):
|
||||||
|
billing_state = values['billing_partner_state'].code if values.get('billing_partner_state') else ''
|
||||||
|
|
||||||
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
authorize_tx_values = dict(values)
|
authorize_tx_values = dict(values)
|
||||||
temp_authorize_tx_values = {
|
temp_authorize_tx_values = {
|
||||||
'x_login': self.authorize_login,
|
'x_login': self.authorize_login,
|
||||||
'x_trans_key': self.authorize_transaction_key,
|
'x_trans_key': self.authorize_transaction_key,
|
||||||
'x_amount': str(values['amount']),
|
'x_amount': float_repr(values['amount'], values['currency'].decimal_places if values['currency'] else 2),
|
||||||
'x_show_form': 'PAYMENT_FORM',
|
'x_show_form': 'PAYMENT_FORM',
|
||||||
'x_type': 'AUTH_CAPTURE' if not self.capture_manually else 'AUTH_ONLY',
|
'x_type': 'AUTH_CAPTURE' if not self.capture_manually else 'AUTH_ONLY',
|
||||||
'x_method': 'CC',
|
'x_method': 'CC',
|
||||||
@ -83,7 +92,7 @@ class PaymentAcquirerAuthorize(models.Model):
|
|||||||
'first_name': values.get('partner_first_name'),
|
'first_name': values.get('partner_first_name'),
|
||||||
'last_name': values.get('partner_last_name'),
|
'last_name': values.get('partner_last_name'),
|
||||||
'phone': values.get('partner_phone'),
|
'phone': values.get('partner_phone'),
|
||||||
'state': values.get('partner_state') and values['partner_state'].code or '',
|
'state': state,
|
||||||
'billing_address': values.get('billing_partner_address'),
|
'billing_address': values.get('billing_partner_address'),
|
||||||
'billing_city': values.get('billing_partner_city'),
|
'billing_city': values.get('billing_partner_city'),
|
||||||
'billing_country': values.get('billing_partner_country') and values.get('billing_partner_country').name or '',
|
'billing_country': values.get('billing_partner_country') and values.get('billing_partner_country').name or '',
|
||||||
@ -92,7 +101,7 @@ class PaymentAcquirerAuthorize(models.Model):
|
|||||||
'billing_first_name': values.get('billing_partner_first_name'),
|
'billing_first_name': values.get('billing_partner_first_name'),
|
||||||
'billing_last_name': values.get('billing_partner_last_name'),
|
'billing_last_name': values.get('billing_partner_last_name'),
|
||||||
'billing_phone': values.get('billing_partner_phone'),
|
'billing_phone': values.get('billing_partner_phone'),
|
||||||
'billing_state': values.get('billing_partner_state') and values['billing_partner_state'].code or '',
|
'billing_state': billing_state,
|
||||||
}
|
}
|
||||||
temp_authorize_tx_values['returndata'] = authorize_tx_values.pop('return_url', '')
|
temp_authorize_tx_values['returndata'] = authorize_tx_values.pop('return_url', '')
|
||||||
temp_authorize_tx_values['x_fp_hash'] = self._authorize_generate_hashing(temp_authorize_tx_values)
|
temp_authorize_tx_values['x_fp_hash'] = self._authorize_generate_hashing(temp_authorize_tx_values)
|
||||||
|
@ -51,7 +51,7 @@ class AuthorizeForm(AuthorizeCommon):
|
|||||||
form_values = {
|
form_values = {
|
||||||
'x_login': self.authorize.authorize_login,
|
'x_login': self.authorize.authorize_login,
|
||||||
'x_trans_key': self.authorize.authorize_transaction_key,
|
'x_trans_key': self.authorize.authorize_transaction_key,
|
||||||
'x_amount': '320.0',
|
'x_amount': '56.16',
|
||||||
'x_show_form': 'PAYMENT_FORM',
|
'x_show_form': 'PAYMENT_FORM',
|
||||||
'x_type': 'AUTH_CAPTURE',
|
'x_type': 'AUTH_CAPTURE',
|
||||||
'x_method': 'CC',
|
'x_method': 'CC',
|
||||||
@ -87,7 +87,7 @@ class AuthorizeForm(AuthorizeCommon):
|
|||||||
|
|
||||||
form_values['x_fp_hash'] = self._authorize_generate_hashing(form_values)
|
form_values['x_fp_hash'] = self._authorize_generate_hashing(form_values)
|
||||||
# render the button
|
# render the button
|
||||||
res = self.authorize.render('SO004', 320.0, self.currency_usd.id, values=self.buyer_values)
|
res = self.authorize.render('SO004', 56.16, self.currency_usd.id, values=self.buyer_values)
|
||||||
# check form result
|
# check form result
|
||||||
tree = objectify.fromstring(res)
|
tree = objectify.fromstring(res)
|
||||||
|
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from flectra.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
|
||||||
|
@ -14,4 +14,5 @@
|
|||||||
'data/payment_acquirer_data.xml',
|
'data/payment_acquirer_data.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
'post_init_hook': 'create_missing_journal_for_acquirers',
|
||||||
}
|
}
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from flectra.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
|
||||||
|
@ -14,4 +14,5 @@
|
|||||||
'data/payment_acquirer_data.xml',
|
'data/payment_acquirer_data.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
'post_init_hook': 'create_missing_journal_for_acquirers',
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ class OgoneController(http.Controller):
|
|||||||
def feedback(self, **kwargs):
|
def feedback(self, **kwargs):
|
||||||
try:
|
try:
|
||||||
tx = request.env['payment.transaction'].sudo()._ogone_form_get_tx_from_data(kwargs)
|
tx = request.env['payment.transaction'].sudo()._ogone_form_get_tx_from_data(kwargs)
|
||||||
tx._ogone_s2s_validate()
|
tx._ogone_s2s_validate_tree(kwargs)
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
return 'ko'
|
return 'ko'
|
||||||
return 'ok'
|
return 'ok'
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from flectra.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
|
||||||
|
@ -14,4 +14,5 @@
|
|||||||
'data/payment_acquirer_data.xml',
|
'data/payment_acquirer_data.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
'post_init_hook': 'create_missing_journal_for_acquirers',
|
||||||
}
|
}
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from flectra.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
|
||||||
|
@ -17,4 +17,5 @@
|
|||||||
'views/payment_payumoney_templates.xml',
|
'views/payment_payumoney_templates.xml',
|
||||||
'data/payment_acquirer_data.xml',
|
'data/payment_acquirer_data.xml',
|
||||||
],
|
],
|
||||||
|
'post_init_hook': 'create_missing_journal_for_acquirers',
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from flectra.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
|
||||||
|
@ -20,4 +20,5 @@ payments acquirer using Worldline SIPS.""",
|
|||||||
'data/payment_acquirer_data.xml',
|
'data/payment_acquirer_data.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
'post_init_hook': 'create_missing_journal_for_acquirers',
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,16 @@ class AcquirerSips(models.Model):
|
|||||||
return self.environment == 'prod' and self.sips_prod_url or self.sips_test_url
|
return self.environment == 'prod' and self.sips_prod_url or self.sips_test_url
|
||||||
|
|
||||||
|
|
||||||
|
class PaymentTransactionSips(models.Model):
|
||||||
|
_inherit = 'payment.transaction'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_next_reference(self, reference, acquirer=None):
|
||||||
|
if acquirer and acquirer.provider == 'sips':
|
||||||
|
reference = re.sub(r'[^0-9a-zA-Z]+', 'x' , reference)
|
||||||
|
return super(PaymentTransactionSips, self)._get_next_reference(reference, acquirer=acquirer)
|
||||||
|
|
||||||
|
|
||||||
class TxSips(models.Model):
|
class TxSips(models.Model):
|
||||||
_inherit = 'payment.transaction'
|
_inherit = 'payment.transaction'
|
||||||
|
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from flectra.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
|
||||||
|
@ -15,4 +15,5 @@
|
|||||||
],
|
],
|
||||||
'images': ['static/description/icon.png'],
|
'images': ['static/description/icon.png'],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
'post_init_hook': 'create_missing_journal_for_acquirers',
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ from flectra import api, fields, models, _
|
|||||||
from flectra.addons.payment.models.payment_acquirer import ValidationError
|
from flectra.addons.payment.models.payment_acquirer import ValidationError
|
||||||
from flectra.exceptions import UserError
|
from flectra.exceptions import UserError
|
||||||
from flectra.tools.safe_eval import safe_eval
|
from flectra.tools.safe_eval import safe_eval
|
||||||
|
from flectra.tools.float_utils import float_round
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -41,12 +42,12 @@ class PaymentAcquirerStripe(models.Model):
|
|||||||
stripe_tx_values = dict(tx_values)
|
stripe_tx_values = dict(tx_values)
|
||||||
temp_stripe_tx_values = {
|
temp_stripe_tx_values = {
|
||||||
'company': self.company_id.name,
|
'company': self.company_id.name,
|
||||||
'amount': tx_values.get('amount'),
|
'amount': tx_values['amount'], # Mandatory
|
||||||
'currency': tx_values.get('currency') and tx_values.get('currency').name or '',
|
'currency': tx_values['currency'].name, # Mandatory anyway
|
||||||
'currency_id': tx_values.get('currency') and tx_values.get('currency').id or '',
|
'currency_id': tx_values['currency'].id, # same here
|
||||||
'address_line1': tx_values.get('partner_address'),
|
'address_line1': tx_values.get('partner_address'), # Any info of the partner is not mandatory
|
||||||
'address_city': tx_values.get('partner_city'),
|
'address_city': tx_values.get('partner_city'),
|
||||||
'address_country': tx_values.get('partner_country') and tx_values['partner_country'].name or '',
|
'address_country': tx_values.get('partner_country') and tx_values.get('partner_country').name or '',
|
||||||
'email': tx_values.get('partner_email'),
|
'email': tx_values.get('partner_email'),
|
||||||
'address_zip': tx_values.get('partner_zip'),
|
'address_zip': tx_values.get('partner_zip'),
|
||||||
'name': tx_values.get('partner_name'),
|
'name': tx_values.get('partner_name'),
|
||||||
@ -106,7 +107,7 @@ class PaymentTransactionStripe(models.Model):
|
|||||||
def _create_stripe_charge(self, acquirer_ref=None, tokenid=None, email=None):
|
def _create_stripe_charge(self, acquirer_ref=None, tokenid=None, email=None):
|
||||||
api_url_charge = 'https://%s/charges' % (self.acquirer_id._get_stripe_api_url())
|
api_url_charge = 'https://%s/charges' % (self.acquirer_id._get_stripe_api_url())
|
||||||
charge_params = {
|
charge_params = {
|
||||||
'amount': int(self.amount if self.currency_id.name in INT_CURRENCIES else self.amount*100),
|
'amount': int(self.amount if self.currency_id.name in INT_CURRENCIES else float_round(self.amount * 100, 2)),
|
||||||
'currency': self.currency_id.name,
|
'currency': self.currency_id.name,
|
||||||
'metadata[reference]': self.reference,
|
'metadata[reference]': self.reference,
|
||||||
'description': self.reference,
|
'description': self.reference,
|
||||||
@ -126,7 +127,7 @@ class PaymentTransactionStripe(models.Model):
|
|||||||
@api.multi
|
@api.multi
|
||||||
def stripe_s2s_do_transaction(self, **kwargs):
|
def stripe_s2s_do_transaction(self, **kwargs):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
result = self._create_stripe_charge(acquirer_ref=self.payment_token_id.acquirer_ref)
|
result = self._create_stripe_charge(acquirer_ref=self.payment_token_id.acquirer_ref, email=self.partner_email)
|
||||||
return self._stripe_s2s_validate_tree(result)
|
return self._stripe_s2s_validate_tree(result)
|
||||||
|
|
||||||
|
|
||||||
@ -135,7 +136,7 @@ class PaymentTransactionStripe(models.Model):
|
|||||||
|
|
||||||
refund_params = {
|
refund_params = {
|
||||||
'charge': self.acquirer_reference,
|
'charge': self.acquirer_reference,
|
||||||
'amount': int(self.amount*100), # by default, stripe refund the full amount (we don't really need to specify the value)
|
'amount': int(float_round(self.amount * 100, 2)), # by default, stripe refund the full amount (we don't really need to specify the value)
|
||||||
'metadata[reference]': self.reference,
|
'metadata[reference]': self.reference,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,31 +62,14 @@ class StripeTest(StripeCommon):
|
|||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
# Test: button direct rendering
|
# Test: button direct rendering
|
||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
form_values = {
|
|
||||||
'amount': 320.0,
|
|
||||||
'currency': 'EUR',
|
|
||||||
'address_line1': 'Huge Street 2/543',
|
|
||||||
'address_city': 'Sin City',
|
|
||||||
'address_country': 'Belgium',
|
|
||||||
'email': 'norbert.buyer@example.com',
|
|
||||||
'address_zip': '1000',
|
|
||||||
'name': 'Norbert Buyer',
|
|
||||||
'phone': '0032 12 34 56 78'
|
|
||||||
}
|
|
||||||
|
|
||||||
# render the button
|
# render the button
|
||||||
res = self.stripe.render('SO404', 320.0, self.currency_euro.id, values=self.buyer_values)
|
res = self.stripe.render('SO404', 320.0, self.currency_euro.id, values=self.buyer_values).decode('utf-8')
|
||||||
post_url = "https://checkout.stripe.com/checkout.js"
|
popup_script_src = 'script src="https://checkout.stripe.com/checkout.js"'
|
||||||
email = "norbert.buyer@example.com"
|
|
||||||
# check form result
|
# check form result
|
||||||
if "https://checkout.stripe.com/checkout.js" in res[0]:
|
self.assertIn(popup_script_src, res, "Stripe: popup script not found in template render")
|
||||||
self.assertEqual(post_url, 'https://checkout.stripe.com/checkout.js', 'Stripe: wrong form POST url')
|
|
||||||
# Generated and received
|
# Generated and received
|
||||||
if email in res[0]:
|
self.assertIn(self.buyer_values.get('partner_email'), res, 'Stripe: email input not found in rendered template')
|
||||||
self.assertEqual(
|
|
||||||
email, form_values.get('email'),
|
|
||||||
'Stripe: wrong value for input %s: received %s instead of %s' % (email, email, form_values.get('email'))
|
|
||||||
)
|
|
||||||
|
|
||||||
@unittest.skip("Stripe test disabled: We do not want to overload Stripe with runbot's requests")
|
@unittest.skip("Stripe test disabled: We do not want to overload Stripe with runbot's requests")
|
||||||
def test_30_stripe_form_management(self):
|
def test_30_stripe_form_management(self):
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from flectra.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
|
||||||
|
@ -15,4 +15,5 @@
|
|||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'auto_install': True,
|
'auto_install': True,
|
||||||
|
'post_init_hook': 'create_missing_journal_for_acquirers',
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,19 @@ class TransferPaymentAcquirer(models.Model):
|
|||||||
|
|
||||||
provider = fields.Selection(selection_add=[('transfer', 'Wire Transfer')], default='transfer')
|
provider = fields.Selection(selection_add=[('transfer', 'Wire Transfer')], default='transfer')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _create_missing_journal_for_acquirers(self, company=None):
|
||||||
|
# By default, the wire transfer method uses the default Bank journal.
|
||||||
|
company = company or self.env.user.company_id
|
||||||
|
acquirers = self.env['payment.acquirer'].search(
|
||||||
|
[('provider', '=', 'transfer'), ('journal_id', '=', False), ('company_id', '=', company.id)])
|
||||||
|
|
||||||
|
bank_journal = self.env['account.journal'].search(
|
||||||
|
[('type', '=', 'bank'), ('company_id', '=', company.id)], limit=1)
|
||||||
|
if bank_journal:
|
||||||
|
acquirers.write({'journal_id': bank_journal.id})
|
||||||
|
return super(TransferPaymentAcquirer, self)._create_missing_journal_for_acquirers(company=company)
|
||||||
|
|
||||||
def transfer_get_form_action_url(self):
|
def transfer_get_form_action_url(self):
|
||||||
return '/payment/transfer/feedback'
|
return '/payment/transfer/feedback'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user