Initial commit: Pos packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:50 +02:00
commit 95dfb9edb0
1301 changed files with 264148 additions and 0 deletions

View file

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import pos_mercury
from . import pos_mercury_transaction
from . import pos_session

View file

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from odoo import models, fields, api, _
from odoo.tools.float_utils import float_compare
_logger = logging.getLogger(__name__)
class BarcodeRule(models.Model):
_inherit = 'barcode.rule'
type = fields.Selection(selection_add=[
('credit', 'Credit Card')
], ondelete={'credit': 'set default'})
class PosMercuryConfiguration(models.Model):
_name = 'pos_mercury.configuration'
_description = 'Point of Sale Vantiv Configuration'
name = fields.Char(required=True, help='Name of this Vantiv configuration')
merchant_id = fields.Char(string='Merchant ID', required=True, help='ID of the merchant to authenticate him on the payment provider server')
merchant_pwd = fields.Char(string='Merchant Password', required=True, help='Password of the merchant to authenticate him on the payment provider server')
class PoSPayment(models.Model):
_inherit = "pos.payment"
mercury_card_number = fields.Char(string='Card Number', help='The last 4 numbers of the card used to pay')
mercury_prefixed_card_number = fields.Char(string='Card Number Prefix', compute='_compute_prefixed_card_number', help='The card number used for the payment.')
mercury_card_brand = fields.Char(string='Card Brand', help='The brand of the payment card (e.g. Visa, AMEX, ...)')
mercury_card_owner_name = fields.Char(string='Card Owner Name')
mercury_ref_no = fields.Char(string='Vantiv reference number', help='Payment reference number from Vantiv Pay')
mercury_record_no = fields.Char(string='Vantiv record number', help='Payment record number from Vantiv Pay')
mercury_invoice_no = fields.Char(string='Vantiv invoice number', help='Invoice number from Vantiv Pay')
def _compute_prefixed_card_number(self):
for line in self:
if line.mercury_card_number:
line.mercury_prefixed_card_number = "********" + line.mercury_card_number
else:
line.mercury_prefixed_card_number = ""
class PoSPaymentMethod(models.Model):
_inherit = 'pos.payment.method'
pos_mercury_config_id = fields.Many2one('pos_mercury.configuration', string='Vantiv Credentials', help='The configuration of Vantiv used for this journal')
def _get_payment_terminal_selection(self):
return super(PoSPaymentMethod, self)._get_payment_terminal_selection() + [('mercury', 'Vantiv')]
@api.onchange('use_payment_terminal')
def _onchange_use_payment_terminal(self):
super(PoSPaymentMethod, self)._onchange_use_payment_terminal()
if self.use_payment_terminal != 'mercury':
self.pos_mercury_config_id = False
class PosOrder(models.Model):
_inherit = "pos.order"
@api.model
def _payment_fields(self, order, ui_paymentline):
fields = super(PosOrder, self)._payment_fields(order, ui_paymentline)
fields.update({
'mercury_card_number': ui_paymentline.get('mercury_card_number'),
'mercury_card_brand': ui_paymentline.get('mercury_card_brand'),
'mercury_card_owner_name': ui_paymentline.get('mercury_card_owner_name'),
'mercury_ref_no': ui_paymentline.get('mercury_ref_no'),
'mercury_record_no': ui_paymentline.get('mercury_record_no'),
'mercury_invoice_no': ui_paymentline.get('mercury_invoice_no')
})
return fields

View file

@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import date, timedelta
import requests
from html import unescape
from markupsafe import Markup
from odoo import models, api, service
from odoo.tools.translate import _
from odoo.exceptions import UserError
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, misc
class MercuryTransaction(models.Model):
_name = 'pos_mercury.mercury_transaction'
_description = 'Point of Sale Vantiv Transaction'
def _get_pos_session(self):
pos_session = self.env['pos.session'].search([('state', '=', 'opened'), ('user_id', '=', self.env.uid)], limit=1)
if not pos_session:
raise UserError(_("No opened point of sale session for user %s found.", self.env.user.name))
pos_session.login()
return pos_session
def _get_pos_mercury_config_id(self, config, payment_method_id):
payment_method = config.current_session_id.payment_method_ids.filtered(lambda pm: pm.id == payment_method_id)
if payment_method and payment_method.pos_mercury_config_id:
return payment_method.pos_mercury_config_id
else:
raise UserError(_("No Vantiv configuration associated with the payment method."))
def _setup_request(self, data):
# todo: in master make the client include the pos.session id and use that
pos_session = self._get_pos_session()
config = pos_session.config_id
pos_mercury_config = self._get_pos_mercury_config_id(config, data['payment_method_id'])
data['operator_id'] = pos_session.user_id.login
data['merchant_id'] = pos_mercury_config.sudo().merchant_id
data['merchant_pwd'] = pos_mercury_config.sudo().merchant_pwd
data['memo'] = "Odoo " + service.common.exp_version()['server_version']
def _do_request(self, template, data):
if not data['merchant_id'] or not data['merchant_pwd']:
return "not setup"
# transaction is str()'ed so it's escaped inside of <mer:tran>
xml_transaction = Markup('''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:mer="http://www.mercurypay.com">
<soapenv:Header/>
<soapenv:Body>
<mer:CreditTransaction>
<mer:tran>{transaction}</mer:tran>
<mer:pw>{password}</mer:pw>
</mer:CreditTransaction>
</soapenv:Body>
</soapenv:Envelope>''').format(transaction=str(self.env['ir.qweb']._render(template, data)), password=data['merchant_pwd'])
response = ''
headers = {
'Content-Type': 'text/xml',
'SOAPAction': 'http://www.mercurypay.com/CreditTransaction',
}
url = 'https://w1.mercurypay.com/ws/ws.asmx'
if self.env['ir.config_parameter'].sudo().get_param('pos_mercury.enable_test_env'):
url = 'https://w1.mercurycert.net/ws/ws.asmx'
try:
r = requests.post(url, data=xml_transaction, headers=headers, timeout=65)
r.raise_for_status()
response = unescape(r.content.decode())
except Exception:
response = "timeout"
return response
def _do_reversal_or_voidsale(self, data, is_voidsale):
try:
self._setup_request(data)
except UserError:
return "internal error"
data['is_voidsale'] = is_voidsale
response = self._do_request('pos_mercury.mercury_voidsale', data)
return response
@api.model
def do_payment(self, data):
try:
self._setup_request(data)
except UserError:
return "internal error"
response = self._do_request('pos_mercury.mercury_transaction', data)
return response
@api.model
def do_reversal(self, data):
return self._do_reversal_or_voidsale(data, False)
@api.model
def do_voidsale(self, data):
return self._do_reversal_or_voidsale(data, True)
def do_return(self, data):
try:
self._setup_request(data)
except UserError:
return "internal error"
response = self._do_request('pos_mercury.mercury_return', data)
return response

View file

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class PosSession(models.Model):
_inherit = 'pos.session'
def _loader_params_pos_payment_method(self):
result = super()._loader_params_pos_payment_method()
result['search_params']['fields'].append('pos_mercury_config_id')
return result