From 67908544ec7c903bcee083b92a7d501c4b09cf2c Mon Sep 17 00:00:00 2001 From: Martin Quinteros Date: Wed, 11 Jun 2025 12:19:50 -0300 Subject: [PATCH 1/5] [ADD] new module pos_financial_surcharge --- pos_financial_surcharge/__init__.py | 1 + pos_financial_surcharge/__manifest__.py | 19 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 209 bytes pos_financial_surcharge/models/__init__.py | 5 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 382 bytes .../__pycache__/account_card.cpython-312.pyc | Bin 0 -> 1665 bytes .../account_card_installment.cpython-312.pyc | Bin 0 -> 1517 bytes .../pos_payment_method.cpython-312.pyc | Bin 0 -> 922 bytes .../__pycache__/pos_session.cpython-312.pyc | Bin 0 -> 1030 bytes .../__pycache__/res_company.cpython-312.pyc | Bin 0 -> 834 bytes .../models/account_card.py | 30 +++ .../models/account_card_installment.py | 24 ++ .../models/pos_payment_method.py | 12 + pos_financial_surcharge/models/pos_session.py | 10 + pos_financial_surcharge/models/res_company.py | 9 + .../static/src/app/financial_surcharge.js | 74 +++++++ .../financial_surcharge_popup.js | 48 ++++ .../financial_surcharge_popup.xml | 29 +++ .../static/src/overrides/models.js | 5 + .../views/card_installment_view.xml | 5 + .../static/src/app/payment_mercado_pago_qr.js | 205 ++++++++++++++++++ pos_mercado_pago_qr/static/src/app/pos_bus.js | 15 -- .../static/src/app/pos_store.js | 19 ++ 23 files changed, 495 insertions(+), 15 deletions(-) create mode 100644 pos_financial_surcharge/__init__.py create mode 100644 pos_financial_surcharge/__manifest__.py create mode 100644 pos_financial_surcharge/__pycache__/__init__.cpython-312.pyc create mode 100644 pos_financial_surcharge/models/__init__.py create mode 100644 pos_financial_surcharge/models/__pycache__/__init__.cpython-312.pyc create mode 100644 pos_financial_surcharge/models/__pycache__/account_card.cpython-312.pyc create mode 100644 pos_financial_surcharge/models/__pycache__/account_card_installment.cpython-312.pyc create mode 100644 pos_financial_surcharge/models/__pycache__/pos_payment_method.cpython-312.pyc create mode 100644 pos_financial_surcharge/models/__pycache__/pos_session.cpython-312.pyc create mode 100644 pos_financial_surcharge/models/__pycache__/res_company.cpython-312.pyc create mode 100644 pos_financial_surcharge/models/account_card.py create mode 100644 pos_financial_surcharge/models/account_card_installment.py create mode 100644 pos_financial_surcharge/models/pos_payment_method.py create mode 100644 pos_financial_surcharge/models/pos_session.py create mode 100644 pos_financial_surcharge/models/res_company.py create mode 100644 pos_financial_surcharge/static/src/app/financial_surcharge.js create mode 100644 pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js create mode 100644 pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.xml create mode 100644 pos_financial_surcharge/static/src/overrides/models.js create mode 100644 pos_financial_surcharge/views/card_installment_view.xml create mode 100644 pos_mercado_pago_qr/static/src/app/payment_mercado_pago_qr.js delete mode 100644 pos_mercado_pago_qr/static/src/app/pos_bus.js create mode 100644 pos_mercado_pago_qr/static/src/app/pos_store.js diff --git a/pos_financial_surcharge/__init__.py b/pos_financial_surcharge/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/pos_financial_surcharge/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/pos_financial_surcharge/__manifest__.py b/pos_financial_surcharge/__manifest__.py new file mode 100644 index 0000000..79e7eb1 --- /dev/null +++ b/pos_financial_surcharge/__manifest__.py @@ -0,0 +1,19 @@ +{ + 'name': 'Pos Financial Surchage', + 'version': "18.0.1.0.0", + 'category': 'Sales/Point of Sale', + 'sequence': 6, + 'summary': 'Add pos finanacial surcharge', + 'data': [ + 'views/card_installment_view.xml', + ], + 'depends': ['point_of_sale', 'card_installment'], + 'installable': True, + 'assets': { + 'point_of_sale._assets_pos': [ + 'pos_financial_surcharge/static/src/**/*', + 'pos_financial_surcharge/static/src/**/**/*', + ], + }, + 'license': 'LGPL-3', +} diff --git a/pos_financial_surcharge/__pycache__/__init__.cpython-312.pyc b/pos_financial_surcharge/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3fe17a4606a3b60b5ba0bc000a4b7c2a9816e72 GIT binary patch literal 209 zcmX@j%ge<81TLHHGem*(V-N=hn4pZ$0zk%eh7^Vr#vF!R#wbQchDs()=9i2>VNJ$c zY`OUq)%)HE!_;|g7%3B;ZK$}W) bQtgU3fX0JtF9tC_Ff%eT-eV9cVgqsj5YRUH literal 0 HcmV?d00001 diff --git a/pos_financial_surcharge/models/__init__.py b/pos_financial_surcharge/models/__init__.py new file mode 100644 index 0000000..3867e85 --- /dev/null +++ b/pos_financial_surcharge/models/__init__.py @@ -0,0 +1,5 @@ +from . import account_card +from . import account_card_installment +from . import pos_session +from . import pos_payment_method +from . import res_company diff --git a/pos_financial_surcharge/models/__pycache__/__init__.cpython-312.pyc b/pos_financial_surcharge/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00ea1690b23e293ac42193a68d577a78a0d7bee4 GIT binary patch literal 382 zcmYk2Jx;_h5QWG2Tg2`*h=Qe}LP~=#fDqyUbTnx!%Q3`~eg@WXpwe`=kIx>H)DTnx95Px!&A4%^DCME%8qb&MA$B*jx85upmQF&Z50Y zw&!E&G9zQ>Qo!2Fz=ShBx8rkNP2h8!a8E!sd=hL_U0WXDV+W-In literal 0 HcmV?d00001 diff --git a/pos_financial_surcharge/models/__pycache__/account_card.cpython-312.pyc b/pos_financial_surcharge/models/__pycache__/account_card.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d8a1defedf7c2aadfde284c6c36e0ead886f371 GIT binary patch literal 1665 zcmaJ>O>7%Q6rR~X+Z(5jOF~KsVq3YDR<&$#hpInO_z|QLky6D{wVHay@rL!TJG&0D zWvU=WBFFUP3qpufQF`FmBjSX_B~}i#=0YS+++0#FoS3(3CvilX!`nA+zBli^Z{FMA zrl(CL*VT7#)qhbD`U5uOrH_>3J5uQ(iYWF_16OcEsVEJ#qGF_=4-r*%5LFr8OphH; zzx(n|SQWjk+4`TL+Nv-b0cBn&RMK>XQFEC`!z!GO*Gc4EY3U(`Dwv{*LUBb^5Ysl} zoX6ngOBJe8?U{ZO(O{{~nMn-nsIi0gN9IcAHJH>Hj7ifws4~^g*ru4eU9AQ!Ke|ge z?PN)^DOF)Nb9;-p9(m|7%k{05d#jxf?$%lL3(Kup?Vx3SN&Lu)>TYNOTngWM$gEb# zDBzdb436lFEi@}jD8rYbCT;P}{(qK7unDzfqN zkH}Vp7Ba6UG)f{8f{SJSvtF(T4OR|l5R|K}FbW!F&YD5!MgezOSeB~OB<%+Cqgb!G zKJlwA@tm;5t98ORSUGvS@~C(Rswp+wV$SgbLLImPB04l^5ZCWqIm1}#KOxgHwJ;^c zHag1XcQeOQMH4kb@^Ovu>`)`cKVVs>vMf1LTq-o5G?>V_ei#wYgX|rbhCD0bw!$HR z$W#Nr=5ByJ;ezMDd_L=>7ymcw1aeV&hY(;J{iP{0<`5|vljo&jB6VowIQ8$O(nDRe z2@iH|b@Bel`jsLPfIqjonBupjjViHJW2u2W@l?AC)mxa)%NxeybLN(=(p9Pc%ovY{ z+@-!%RwbEZ%}y_OO!<7JgeiVOdUN24zahnvjfDm;ED4QIAcn1Sm@qysg_sH%kv-yY zCP7-vdW;lC(h3|rLQM)wu7v&zlO55y{Q7RW1l~<4w$UIz_ac9zpTF^R@i711u5o7n z(?Y*^^Z8c4xb*X*e(~d93;p7~!~DvwakTi!!RPn;i}&{yU(6QzvxV>1o~}K=b}(Bw zoV_)8bK#|~!Xv5~bMU~K*vF5$Y-dHLGGRHkB;WA z?QQiQKY2XJT^US&I+&SzmC`O52ioi~i;T=S>tC76=VE6D)dp| Um2nxHd)Ir#C&j;!RAc9V0nyTruK)l5 literal 0 HcmV?d00001 diff --git a/pos_financial_surcharge/models/__pycache__/account_card_installment.cpython-312.pyc b/pos_financial_surcharge/models/__pycache__/account_card_installment.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b6b7d9dcff639cd4db43c986e71b6f3f33e6cc5f GIT binary patch literal 1517 zcmaJ>O=ufe5Pt9NkEC^6M@CN4kbnzzV6l+sH3YYzCTR;*O9KfJG>^@`Cwtx1uJTq2 z*eKwDp*|)j^-xObDaAeXm}8HHULt&md6x!y>P@MVOHZBIUB$9#IZh_#BEuRMI6`tUeb-*btOA-5_Dq~$fOP;?|55y zODea5$cy~I3whF0{)VU4Wj!lZJ$T7qSB}I(YN5q2uNC7o#xCPAup$l0`LYtA-mbxH<`#};b zJu{W1UU&nD`>|}b0zUxIdiJgtea%IXqib2dZjJa{E0utlJ( z1MeIkrS0=?AG3Lk?f}~%hvkLm<&|N1<;mJX`Tae6O8@1;u)6y6{;+!O*L%b2C%-KX ztDhf~zu2>1tbO|I%Ui>>TaVVBSC)pAr5|oSx%qVYS!L;<^5Nl`3ok7L`)HJ$!>{-; zJ?(45E$JfXIF+1hhjUn^UWoE3&hPcSF#l47ecF&J=qd!Nt#sp#ili=rH66|L)U*f+ z*FR+#l3WJ1OAhDUosVBw#cysuy#10fYkq7Hdv1i%s7UNmW;ZM+x1;3PRyKs`VGY;P zCjKoHj#Qq`XZWwoF2wyfN^OwIut8I;mL$i-K7g*uNe4XTf;uL+20 F*K_z~4paLQA5;GwrDnMeO1Fb5tc{y43C2?i@+&c$Ilp#YU z+5rg$RQ?6jjemnBQ3sG=Ktc>`l>(w-;+^fZ6$>Z%yYKzD_ddV74~<40aM|zgcUK6& zH)YJ0QIcsJ$sYLNlMo!WYaxj=Ml4fPp^SBlWYviKER^2qRIN0oSCPCxO$;33gQNMxsq8?T_}aGV=sWP7IK~sO zD%sf$E;rnM#Nu>~rCsh1&Lv#7lET%Ba-Uaf&8sUl>YLs~ctiFe&D{!i$q-)RRQ5}c zPda3P96M?2mX@7w2eBJ_fg4iU6JFO99X7c5&=vmjab)>8f(H!QOPI*6&<;y!sfngc zL|6xFu#kCaz+=qR(P(FLlzO2nC8bi0VtMq3+Fc$ooBN#GUQebxvIWCG3{oxvCT*m% z!Z|r58grH2ZI zwg*8ErT>D`f5892OG^=CE*13RP1p*>lQYR~+k*r7^1XSJ_kHgr`O;|AfLgcr?{#Ac zzz-dampvlGSBe|}0tie1g8^hlU@$WemOQ9$*uL{i2fi&$+>50X#WW#8IxHhJ5fUO}`m(}Nj@{)Qla1s-o$5~ozgqO+yjHJPELGA9S1}O+20f9kaU{Y(X z7|hiN0N4c)B*wOTINA=JU9b*=$_nsJSzF4)it?ny1{1wZv_u(%#+4#kRA0kc1^e&- zP_fHlvu|YD5eD#2X!2VSwGENA?9Ro<7(83Q<38&VKmKsL)xO> zqg+m*B*O$LGe}JVqjHqWt0ia>$!eITQQSc>kyV7k1Pg(X(Ds_mt-mkZ-Hg$8MzX9O z_C=ntHm9mooM$|yqOGg;@P<|_coe5N4P%@j(dS_o^A2s7f<<~XjaF}iUsDQ$=F-Ku zTK~1_s#D^og7Yo#)0^CTbL`E2Ts`q_f1W>jN{{YD$6j>gMniQ1_^xyjN-?7d$r{p) z_7lzP2)*v(WR!7HoOUUXbFQ0|c44ouBO9XugfvMI@*%&jP6+)X7Wkw>-WY@aEkskn z`8N3G&1^mSZo50HZ&!x~v^#KU0>?WmD4!ZXvgkLYr3YE)a!c>)GThNnQ6N2?ZqfCS z_Vni}xNe_UHRunKPv}GTGyN|VRz88Uy!IyDyJf@kys1KMTHI3b+k_CFg1N6??i4Ja WfaNpCh8_5R`e5e6%pah6QRE*#a}HGi literal 0 HcmV?d00001 diff --git a/pos_financial_surcharge/models/__pycache__/res_company.cpython-312.pyc b/pos_financial_surcharge/models/__pycache__/res_company.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db61b90ef1f0c387f977bcf7defb0d05c5e210cd GIT binary patch literal 834 zcmZuvy^9k;6o0du&Fw|wHm9JZNV_80Qqbc#w9v+YNn^GdhMh^W$9_4pizZwV;o$BH zK?}*hpyW@n$zdbYC}?A)kP{9oXEuqs!hw17d*8qJn2+^(4b*9i2g9H0{ic=mu~uNQ zr@#~_P!vIod=wkLfxv)AK#e1yCNmDI>!i&)Gnd$}j4H16Lo2Q!O_GJO8mE*+BG?22 ztfFDDudK(a9|Ipz@C}N5lT{8%o|h8al%}Z{6e3S!k26&&%u^mR;pwUw8O1EgOWY3=k^~`%He|fzidCM#tI1p0 zi0dtmo14|N{!4R9MTv$I7ZdpI?3}(hbMCzFeRA&3??0X2>zp~Ax!qYPKe)EEF-}O# zFqSopRo5cY_$tQF3lgn7HV%^^<6+MAh|(&_m1JanHGaUN2xAxVx{45b<@@}al3ahS zQdG2~sl>$*e0Ca>$6qb`xc92JFp$+n3lpl&c?r3*PHDTAtmrkCeYvh6tta~Pl@&k0 rGF11G_U@AW9>1wnO+egI;+KgK`T~vfs)f4f&5dc}ZR0m+T;~1-`K{6W literal 0 HcmV?d00001 diff --git a/pos_financial_surcharge/models/account_card.py b/pos_financial_surcharge/models/account_card.py new file mode 100644 index 0000000..1a0417b --- /dev/null +++ b/pos_financial_surcharge/models/account_card.py @@ -0,0 +1,30 @@ +from odoo import models, api, fields + + +class AccountCard(models.Model): + _inherit = "account.card" + + available_in_pos = fields.Boolean( + string='Available in POS', + help='Check if you want this card can be used in the Point of Sale.', + default=False + ) + + @api.model + def _load_pos_data_domain(self, data): + return [] + # return self.env['account.card']._check_company_domain(data['pos.config']['data'][0]['company_id']) + [('available_in_pos', '=', True)] + + @api.model + def _load_pos_data_fields(self, config_id): + return [ + 'id', 'name', 'installment_ids' + ] + + def _load_pos_data(self, data): + domain = self._load_pos_data_domain(data) + fields = self._load_pos_data_fields(data['pos.config']['data'][0]['id']) + return { + 'data': self.search_read(domain, fields, load=False) if domain is not False else [], + 'fields': fields, + } diff --git a/pos_financial_surcharge/models/account_card_installment.py b/pos_financial_surcharge/models/account_card_installment.py new file mode 100644 index 0000000..f01858e --- /dev/null +++ b/pos_financial_surcharge/models/account_card_installment.py @@ -0,0 +1,24 @@ +from odoo import models, api + + +class AccountCard(models.Model): + _inherit = "account.card.installment" + + @api.model + def _load_pos_data_domain(self, data): + #return self.env['account.card']._check_company_domain(data['pos.config']['data'][0]['company_id']) + return [] + + @api.model + def _load_pos_data_fields(self, config_id): + return [ + 'id', 'card_id', 'name', 'divisor', 'installment', 'surcharge_coefficient', 'bank_discount' + ] + + def _load_pos_data(self, data): + domain = self._load_pos_data_domain(data) + fields = self._load_pos_data_fields(data['pos.config']['data'][0]['id']) + return { + 'data': self.search_read(domain, fields, load=False) if domain is not False else [], + 'fields': fields, + } diff --git a/pos_financial_surcharge/models/pos_payment_method.py b/pos_financial_surcharge/models/pos_payment_method.py new file mode 100644 index 0000000..54cb33f --- /dev/null +++ b/pos_financial_surcharge/models/pos_payment_method.py @@ -0,0 +1,12 @@ +import logging + +from odoo import models + +_logger = logging.getLogger(__name__) + + +class PosPaymentMethod(models.Model): + _inherit = 'pos.payment.method' + + def _get_payment_terminal_selection(self): + return super()._get_payment_terminal_selection() + [('financial_surcharge', 'Card financial surcharge')] diff --git a/pos_financial_surcharge/models/pos_session.py b/pos_financial_surcharge/models/pos_session.py new file mode 100644 index 0000000..46f9b1b --- /dev/null +++ b/pos_financial_surcharge/models/pos_session.py @@ -0,0 +1,10 @@ +from odoo import api, fields, models, _, Command +from odoo.exceptions import AccessDenied, AccessError, UserError, ValidationError + + +class PosSession(models.Model): + _inherit = 'pos.session' + + @api.model + def _load_pos_data_models(self, config_id): + return super()._load_pos_data_models(config_id) + ['account.card', 'account.card.installment'] diff --git a/pos_financial_surcharge/models/res_company.py b/pos_financial_surcharge/models/res_company.py new file mode 100644 index 0000000..1b5f4bb --- /dev/null +++ b/pos_financial_surcharge/models/res_company.py @@ -0,0 +1,9 @@ +from odoo import api, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + @api.model + def _load_pos_data_fields(self, config_id): + return super()._load_pos_data_fields(config_id) + ['product_surcharge_id'] diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge.js b/pos_financial_surcharge/static/src/app/financial_surcharge.js new file mode 100644 index 0000000..49993e0 --- /dev/null +++ b/pos_financial_surcharge/static/src/app/financial_surcharge.js @@ -0,0 +1,74 @@ +/** @odoo-module */ +import { _t } from "@web/core/l10n/translation"; +import { PaymentInterface } from "@point_of_sale/app/payment/payment_interface"; +import { ask } from "@point_of_sale/app/store/make_awaitable_dialog"; +import { AlertDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; +import { FinancialSurchargePopup } from "@pos_financial_surcharge/app/financial_surcharge_popup/financial_surcharge_popup"; + + +export class FinancialSurcharge extends PaymentInterface { + + async _get_cards(){ + const res = [] + const installment_ids = this.pos.models['account.card.installment'].getAll() + for (const card of this.pos.models['account.card'].getAll()) { + res.push({ + id: card.id, + name: card.name, + installments: installment_ids.filter((installment) => { + return installment.card_id.id == card.id; + }).map((installment) => { + return { + id: installment.id, + name: installment.name, + divisor: installment.divisor, + installment: installment.installment, + surcharge_coefficient: installment.surcharge_coefficient, + bank_discount: installment.bank_discount, + }; + }), + }) + } + debugger; + return res; + } + async send_payment_request(cid) { + await super.send_payment_request(...arguments); + const line = this.pos.get_order().get_selected_paymentline(); + try { + // During payment creation, user can't cancel the payment intent + line.set_payment_status("waitingCapture"); + return await ask( + this.env.services.dialog, + { + title: 'Select payment intallment ', + line: line, + cards: await this._get_cards(), + order: line.pos_order_id, + pos: this.pos, + }, + {}, + FinancialSurchargePopup + ).then((result) => { + return result; + }); + + } catch (error) { + console.error(error); + this._showMsg('error', "System error"); + return false; + } + } + + async send_payment_cancel(order, cid) { + await super.send_payment_cancel(order, cid); + return true; + } + // private methods + _showMsg(msg, title) { + this.env.services.dialog.add(AlertDialog, { + title: "Error " + title, + body: msg, + }); + } +} \ No newline at end of file diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js new file mode 100644 index 0000000..730a331 --- /dev/null +++ b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js @@ -0,0 +1,48 @@ +import { useState } from "@odoo/owl"; +import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; +import { _t } from "@web/core/l10n/translation"; + +export class FinancialSurchargePopup extends ConfirmationDialog { + static template = "point_of_sale.FinancialSurchargeConfirmationDialog"; + static props = { + ...ConfirmationDialog.props, + line: Object, + order: Object, + cards: Object, + pos: Object, + }; + async _confirm() { + debugger; + const instalments = this.props.line.models['account.card.installment'].getAllBy('id') + this.props.line.amount=this.state.raw_amount * instalments[this.state.selected_installment].surcharge_coefficient; + const diff_amount = this.state.raw_amount - this.props.line.amount + debugger; + await this.props.pos.addLineToCurrentOrder({ + product_id: 26,// filo crea un data que te cree un producto recargo haber como lo podes relacionar + qty: 1, + price: diff_amount, + note: instalments[this.state.selected_installment].name, + }); + + this.props.line.set_payment_status("done"); + return this.execButton(this.props.confirm); + } + + static defaultProps = { + ...ConfirmationDialog.defaultProps, + confirmLabel: _t("Confirm Payment"), + cancelLabel: _t("Cancel Payment"), + title: _t("Card register"), + }; + formatCurrency(amount) { + return this.env.utils.formatCurrency(amount); + } + setup() { + super.setup(); + this.props.body = _t(" %s", this.props.title); + this.amount = this.env.utils.formatCurrency(this.props.line.amount); + this.raw_amount = this.props.line.amount; + this.cards = this.props.cards; + this.state = useState({raw_amount : this.props.line.amount}); + } +} diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.xml b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.xml new file mode 100644 index 0000000..f4560b8 --- /dev/null +++ b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.xml @@ -0,0 +1,29 @@ + + + + + +
+

+

+ Amount: + +
+ +
+
+
+ +
diff --git a/pos_financial_surcharge/static/src/overrides/models.js b/pos_financial_surcharge/static/src/overrides/models.js new file mode 100644 index 0000000..a44546a --- /dev/null +++ b/pos_financial_surcharge/static/src/overrides/models.js @@ -0,0 +1,5 @@ +/** @odoo-module */ +import { register_payment_method } from "@point_of_sale/app/store/pos_store"; +import { FinancialSurcharge } from "@pos_financial_surcharge/app/financial_surcharge"; + +register_payment_method("financial_surcharge", FinancialSurcharge); diff --git a/pos_financial_surcharge/views/card_installment_view.xml b/pos_financial_surcharge/views/card_installment_view.xml new file mode 100644 index 0000000..b8069de --- /dev/null +++ b/pos_financial_surcharge/views/card_installment_view.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/pos_mercado_pago_qr/static/src/app/payment_mercado_pago_qr.js b/pos_mercado_pago_qr/static/src/app/payment_mercado_pago_qr.js new file mode 100644 index 0000000..1a8c08d --- /dev/null +++ b/pos_mercado_pago_qr/static/src/app/payment_mercado_pago_qr.js @@ -0,0 +1,205 @@ +/** @odoo-module */ +import { _t } from "@web/core/l10n/translation"; +import { PaymentInterface } from "@point_of_sale/app/payment/payment_interface"; +import { AlertDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; + +export class PaymentMercadoPagoQR extends PaymentInterface { + async create_payment_qr() { + const order = this.pos.get_order(); + const line = order.get_selected_paymentline(); + // Build informations for creating a payment intend on Mercado Pago. + // Data in "external_reference" are send back with the webhook notification + let entropy = Date.now() + Math.random(); + line.externalReference = `QR_${this.pos.pos_session.id}_${line.payment_method.id}_${line.cid}_${entropy}`; + const infos = { + title: order.name, + notification_url: base_url + '/pos_mercado_pago/notification', + description: this.pos.config.company_id[1], + total_amount: line.amount, + items: [{ + sku_number: "0001", + category: "general", + title: this.pos.config.current_session_id[1] + "sale", + description: "odoo sale", + unit_price: line.amount, + quantity: 1, + unit_measure: "unit", + total_amount: line.amount, + }], + external_reference: line.externalReference + }; + + // mp_payment_order_create will call the Mercado Pago api + return await this.env.services.orm.silent.call( + "pos.payment.method", + "mp_payment_order_create", + [[line.payment_method_id.id], infos] + ); + } + + async mp_payment_order_get() { + const line = this.pos.get_order().get_selected_paymentline(); + // mp_payment_intent_get will call the Mercado Pago api + return await this.env.services.orm.silent.call( + "pos.payment.method", + "mp_payment_order_get", + [[line.payment_method_id.id], line.externalReference] + ); + } + + async cancel_payment_order() { + const line = this.pos.get_order().get_selected_paymentline(); + // mp_payment_order_cancel will call the Mercado Pago api + return await this.env.services.orm.silent.call( + "pos.payment.method", + "mp_payment_order_cancel", + [[line.payment_method_id.id], line.externalReference] + ); + } + + async get_payment(payment_id) { + const line = this.pos.get_order().get_selected_paymentline(); + // mp_get_payment_status will call the Mercado Pago api + return await this.env.services.orm.silent.call( + "pos.payment.method", + "mp_get_payment_status", + [[line.payment_method_id.id], payment_id] + ); + } + + setup() { + super.setup(...arguments); + this.webhook_resolver = null; + this.payment_intent = {}; + } + + async send_payment_request(cid) { + await super.send_payment_request(...arguments); + const line = this.pos.get_order().get_selected_paymentline(); + try { + // During payment creation, user can't cancel the payment intent + line.set_payment_status("waitingCapture"); + // Call Mercado Pago to create a payment intent + const payment_order = await this.create_payment_qr(); + if (! payment_order) { + this._showMsg(payment_order.message, "error"); + return false; + } + // Payment intent creation successfull, save it + this.payment_order = payment_order; + // After payment creation, make the payment intent canceling possible + line.set_payment_status("waitingCard"); + // Wait for payment intent status change and return status result + return await new Promise((resolve) => { + this.webhook_resolver = resolve; + }); + } catch (error) { + this._showMsg(error, "System error"); + return false; + } + } + + async send_payment_cancel(order, cid) { + await super.send_payment_cancel(order, cid); + if (!("id" in this.payment_intent)) { + return true; + } + const canceling_status = await this.cancel_payment_intent(); + if ("error" in canceling_status) { + const message = + canceling_status.status === 409 + ? _t("Payment has to be canceled on terminal") + : _t("Payment not found (canceled/finished on terminal)"); + this._showMsg(message, "info"); + return canceling_status.status !== 409; + } + return true; + } + + async handleMercadoPagoQRWebhook() { + const line = this.pos.get_order().get_selected_paymentline(); + const MAX_RETRY = 5; // Maximum number of retries for the "ON_TERMINAL" BUG + const RETRY_DELAY = 1000; // Delay between retries in milliseconds for the "ON_TERMINAL" BUG + + const showMessageAndResolve = (messageKey, status, resolverValue) => { + if (!resolverValue) { + this._showMsg(messageKey, status); + } + line.set_payment_status("done"); + this.webhook_resolver?.(resolverValue); + return resolverValue; + }; + + const handleFinishedPayment = async (paymentIntent) => { + if (paymentIntent.state === "CANCELED") { + return showMessageAndResolve(_t("Payment has been canceled"), "info", false); + } + if (["FINISHED", "PROCESSED"].includes(paymentIntent.state)) { + const payment = await this.get_payment(paymentIntent.payment.id); + if (payment.status === "approved") { + return showMessageAndResolve(_t("Payment has been processed"), "info", true); + } + return showMessageAndResolve(_t("Payment has been rejected"), "info", false); + } + }; + + // No payment intent id means either that the user reload the page or + // it is an old webhook -> trash + if ("id" in this.payment_intent) { + // Call Mercado Pago to get the payment intent status + let last_status_payment_intent = await this.mp_payment_order_get(); + // Bad payment intent id, then it's an old webhook not related with the + // current payment intent -> trash + if (this.payment_intent.id == last_status_payment_intent.id) { + if ( + ["FINISHED", "PROCESSED", "CANCELED"].includes(last_status_payment_intent.state) + ) { + return await handleFinishedPayment(last_status_payment_intent); + } + // BUG Sometimes the Mercado Pago webhook return ON_TERMINAL + // instead of CANCELED/FINISHED when we requested a payment status + // that was actually canceled/finished by the user on the terminal. + // Then the strategy here is to ask Mercado Pago MAX_RETRY times the + // payment intent status, hoping going out of this status + if (["OPEN", "ON_TERMINAL"].includes(last_status_payment_intent.state)) { + return await new Promise((resolve) => { + let retry_cnt = 0; + const s = setInterval(async () => { + last_status_payment_intent = + await this.mp_payment_order_get(); + if ( + ["FINISHED", "PROCESSED", "CANCELED"].includes( + last_status_payment_intent.state + ) + ) { + clearInterval(s); + resolve(await handleFinishedPayment(last_status_payment_intent)); + } + retry_cnt += 1; + if (retry_cnt >= MAX_RETRY) { + clearInterval(s); + resolve( + showMessageAndResolve( + _t("Payment status could not be confirmed"), + "error", + false + ) + ); + } + }, RETRY_DELAY); + }); + } + // If the state does not match any of the expected values + return showMessageAndResolve(_t("Unknown payment status"), "error", false); + } + } + } + + // private methods + _showMsg(msg, title) { + this.env.services.dialog.add(AlertDialog, { + title: "Mercado Pago " + title, + body: msg, + }); + } +} diff --git a/pos_mercado_pago_qr/static/src/app/pos_bus.js b/pos_mercado_pago_qr/static/src/app/pos_bus.js deleted file mode 100644 index 381f21a..0000000 --- a/pos_mercado_pago_qr/static/src/app/pos_bus.js +++ /dev/null @@ -1,15 +0,0 @@ -/** @odoo-module */ -import { patch } from "@web/core/utils/patch"; -import { PosBus } from "@point_of_sale/app/bus/pos_bus_service"; - -patch(PosBus.prototype, { - // Override - dispatch(message) { - super.dispatch(...arguments); - if (message.type === "MERCADO_PAGO_LATEST_MESSAGE") { - this.pos - .getPendingPaymentLine("mercado_pago") - .payment_method.payment_terminal.handleMercadoPagoWebhook(); - } - }, -}); diff --git a/pos_mercado_pago_qr/static/src/app/pos_store.js b/pos_mercado_pago_qr/static/src/app/pos_store.js new file mode 100644 index 0000000..7da6a4e --- /dev/null +++ b/pos_mercado_pago_qr/static/src/app/pos_store.js @@ -0,0 +1,19 @@ +/** @odoo-module */ +import { patch } from "@web/core/utils/patch"; +import { PosStore } from "@point_of_sale/app/store/pos_store"; + +patch(PosStore.prototype, { + // Override + async setup() { + await super.setup(...arguments); + this.data.connectWebSocket("MERCADO_PAGO_LATEST_MESSAGE", (payload) => { + if (payload.config_id === this.config.id) { + const pendingLine = this.getPendingPaymentLine("mercado_pago"); + + if (pendingLine) { + pendingLine.payment_method_id.payment_terminal.handleMercadoPagoQRWebhook(); + } + } + }); + }, +}); From 12218b20b3715ff0d8389c6c044b91cf6a4b237d Mon Sep 17 00:00:00 2001 From: fsuleWitdata Date: Wed, 11 Jun 2025 14:38:41 -0300 Subject: [PATCH 2/5] UPG: product in ticket and note card agrega el producto de recargo(servicio y disponible en pos) , agrega una linea de nota en el ticket para saber que metodo se utilizo y en que cuotas --- pos_financial_surcharge/__manifest__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 209 -> 0 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 382 -> 0 bytes .../__pycache__/account_card.cpython-312.pyc | Bin 1665 -> 0 bytes .../account_card_installment.cpython-312.pyc | Bin 1517 -> 0 bytes .../pos_payment_method.cpython-312.pyc | Bin 922 -> 0 bytes .../__pycache__/pos_session.cpython-312.pyc | Bin 1030 -> 0 bytes .../__pycache__/res_company.cpython-312.pyc | Bin 834 -> 0 bytes .../models/pos_payment_method.py | 21 ++++- pos_financial_surcharge/models/pos_session.py | 2 +- .../static/src/app/financial_surcharge.js | 8 +- .../financial_surcharge_popup.js | 75 +++++++++++++++--- ...inancial_surcharge_payment_method_view.xml | 15 ++++ 13 files changed, 103 insertions(+), 20 deletions(-) delete mode 100644 pos_financial_surcharge/__pycache__/__init__.cpython-312.pyc delete mode 100644 pos_financial_surcharge/models/__pycache__/__init__.cpython-312.pyc delete mode 100644 pos_financial_surcharge/models/__pycache__/account_card.cpython-312.pyc delete mode 100644 pos_financial_surcharge/models/__pycache__/account_card_installment.cpython-312.pyc delete mode 100644 pos_financial_surcharge/models/__pycache__/pos_payment_method.cpython-312.pyc delete mode 100644 pos_financial_surcharge/models/__pycache__/pos_session.cpython-312.pyc delete mode 100644 pos_financial_surcharge/models/__pycache__/res_company.cpython-312.pyc create mode 100644 pos_financial_surcharge/views/financial_surcharge_payment_method_view.xml diff --git a/pos_financial_surcharge/__manifest__.py b/pos_financial_surcharge/__manifest__.py index 79e7eb1..1e86f2b 100644 --- a/pos_financial_surcharge/__manifest__.py +++ b/pos_financial_surcharge/__manifest__.py @@ -2,10 +2,12 @@ 'name': 'Pos Financial Surchage', 'version': "18.0.1.0.0", 'category': 'Sales/Point of Sale', + 'developer': 'Martín Quinteros(Filoquin) , Francisco Sulé', 'sequence': 6, 'summary': 'Add pos finanacial surcharge', 'data': [ 'views/card_installment_view.xml', + 'views/financial_surcharge_payment_method_view.xml', ], 'depends': ['point_of_sale', 'card_installment'], 'installable': True, diff --git a/pos_financial_surcharge/__pycache__/__init__.cpython-312.pyc b/pos_financial_surcharge/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index f3fe17a4606a3b60b5ba0bc000a4b7c2a9816e72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209 zcmX@j%ge<81TLHHGem*(V-N=hn4pZ$0zk%eh7^Vr#vF!R#wbQchDs()=9i2>VNJ$c zY`OUq)%)HE!_;|g7%3B;ZK$}W) bQtgU3fX0JtF9tC_Ff%eT-eV9cVgqsj5YRUH diff --git a/pos_financial_surcharge/models/__pycache__/__init__.cpython-312.pyc b/pos_financial_surcharge/models/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 00ea1690b23e293ac42193a68d577a78a0d7bee4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 382 zcmYk2Jx;_h5QWG2Tg2`*h=Qe}LP~=#fDqyUbTnx!%Q3`~eg@WXpwe`=kIx>H)DTnx95Px!&A4%^DCME%8qb&MA$B*jx85upmQF&Z50Y zw&!E&G9zQ>Qo!2Fz=ShBx8rkNP2h8!a8E!sd=hL_U0WXDV+W-In diff --git a/pos_financial_surcharge/models/__pycache__/account_card.cpython-312.pyc b/pos_financial_surcharge/models/__pycache__/account_card.cpython-312.pyc deleted file mode 100644 index 2d8a1defedf7c2aadfde284c6c36e0ead886f371..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1665 zcmaJ>O>7%Q6rR~X+Z(5jOF~KsVq3YDR<&$#hpInO_z|QLky6D{wVHay@rL!TJG&0D zWvU=WBFFUP3qpufQF`FmBjSX_B~}i#=0YS+++0#FoS3(3CvilX!`nA+zBli^Z{FMA zrl(CL*VT7#)qhbD`U5uOrH_>3J5uQ(iYWF_16OcEsVEJ#qGF_=4-r*%5LFr8OphH; zzx(n|SQWjk+4`TL+Nv-b0cBn&RMK>XQFEC`!z!GO*Gc4EY3U(`Dwv{*LUBb^5Ysl} zoX6ngOBJe8?U{ZO(O{{~nMn-nsIi0gN9IcAHJH>Hj7ifws4~^g*ru4eU9AQ!Ke|ge z?PN)^DOF)Nb9;-p9(m|7%k{05d#jxf?$%lL3(Kup?Vx3SN&Lu)>TYNOTngWM$gEb# zDBzdb436lFEi@}jD8rYbCT;P}{(qK7unDzfqN zkH}Vp7Ba6UG)f{8f{SJSvtF(T4OR|l5R|K}FbW!F&YD5!MgezOSeB~OB<%+Cqgb!G zKJlwA@tm;5t98ORSUGvS@~C(Rswp+wV$SgbLLImPB04l^5ZCWqIm1}#KOxgHwJ;^c zHag1XcQeOQMH4kb@^Ovu>`)`cKVVs>vMf1LTq-o5G?>V_ei#wYgX|rbhCD0bw!$HR z$W#Nr=5ByJ;ezMDd_L=>7ymcw1aeV&hY(;J{iP{0<`5|vljo&jB6VowIQ8$O(nDRe z2@iH|b@Bel`jsLPfIqjonBupjjViHJW2u2W@l?AC)mxa)%NxeybLN(=(p9Pc%ovY{ z+@-!%RwbEZ%}y_OO!<7JgeiVOdUN24zahnvjfDm;ED4QIAcn1Sm@qysg_sH%kv-yY zCP7-vdW;lC(h3|rLQM)wu7v&zlO55y{Q7RW1l~<4w$UIz_ac9zpTF^R@i711u5o7n z(?Y*^^Z8c4xb*X*e(~d93;p7~!~DvwakTi!!RPn;i}&{yU(6QzvxV>1o~}K=b}(Bw zoV_)8bK#|~!Xv5~bMU~K*vF5$Y-dHLGGRHkB;WA z?QQiQKY2XJT^US&I+&SzmC`O52ioi~i;T=S>tC76=VE6D)dp| Um2nxHd)Ir#C&j;!RAc9V0nyTruK)l5 diff --git a/pos_financial_surcharge/models/__pycache__/account_card_installment.cpython-312.pyc b/pos_financial_surcharge/models/__pycache__/account_card_installment.cpython-312.pyc deleted file mode 100644 index b6b7d9dcff639cd4db43c986e71b6f3f33e6cc5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1517 zcmaJ>O=ufe5Pt9NkEC^6M@CN4kbnzzV6l+sH3YYzCTR;*O9KfJG>^@`Cwtx1uJTq2 z*eKwDp*|)j^-xObDaAeXm}8HHULt&md6x!y>P@MVOHZBIUB$9#IZh_#BEuRMI6`tUeb-*btOA-5_Dq~$fOP;?|55y zODea5$cy~I3whF0{)VU4Wj!lZJ$T7qSB}I(YN5q2uNC7o#xCPAup$l0`LYtA-mbxH<`#};b zJu{W1UU&nD`>|}b0zUxIdiJgtea%IXqib2dZjJa{E0utlJ( z1MeIkrS0=?AG3Lk?f}~%hvkLm<&|N1<;mJX`Tae6O8@1;u)6y6{;+!O*L%b2C%-KX ztDhf~zu2>1tbO|I%Ui>>TaVVBSC)pAr5|oSx%qVYS!L;<^5Nl`3ok7L`)HJ$!>{-; zJ?(45E$JfXIF+1hhjUn^UWoE3&hPcSF#l47ecF&J=qd!Nt#sp#ili=rH66|L)U*f+ z*FR+#l3WJ1OAhDUosVBw#cysuy#10fYkq7Hdv1i%s7UNmW;ZM+x1;3PRyKs`VGY;P zCjKoHj#Qq`XZWwoF2wyfN^OwIut8I;mL$i-K7g*uNe4XTf;uL+20 F*K_z~4paLQA5;GwrDnMeO1Fb5tc{y43C2?i@+&c$Ilp#YU z+5rg$RQ?6jjemnBQ3sG=Ktc>`l>(w-;+^fZ6$>Z%yYKzD_ddV74~<40aM|zgcUK6& zH)YJ0QIcsJ$sYLNlMo!WYaxj=Ml4fPp^SBlWYviKER^2qRIN0oSCPCxO$;33gQNMxsq8?T_}aGV=sWP7IK~sO zD%sf$E;rnM#Nu>~rCsh1&Lv#7lET%Ba-Uaf&8sUl>YLs~ctiFe&D{!i$q-)RRQ5}c zPda3P96M?2mX@7w2eBJ_fg4iU6JFO99X7c5&=vmjab)>8f(H!QOPI*6&<;y!sfngc zL|6xFu#kCaz+=qR(P(FLlzO2nC8bi0VtMq3+Fc$ooBN#GUQebxvIWCG3{oxvCT*m% z!Z|r58grH2ZI zwg*8ErT>D`f5892OG^=CE*13RP1p*>lQYR~+k*r7^1XSJ_kHgr`O;|AfLgcr?{#Ac zzz-dampvlGSBe|}0tie1g8^hlU@$WemOQ9$*uL{i2fi&$+>50X#WW#8IxHhJ5fUO}`m(}Nj@{)Qla1s-o$5~ozgqO+yjHJPELGA9S1}O+20f9kaU{Y(X z7|hiN0N4c)B*wOTINA=JU9b*=$_nsJSzF4)it?ny1{1wZv_u(%#+4#kRA0kc1^e&- zP_fHlvu|YD5eD#2X!2VSwGENA?9Ro<7(83Q<38&VKmKsL)xO> zqg+m*B*O$LGe}JVqjHqWt0ia>$!eITQQSc>kyV7k1Pg(X(Ds_mt-mkZ-Hg$8MzX9O z_C=ntHm9mooM$|yqOGg;@P<|_coe5N4P%@j(dS_o^A2s7f<<~XjaF}iUsDQ$=F-Ku zTK~1_s#D^og7Yo#)0^CTbL`E2Ts`q_f1W>jN{{YD$6j>gMniQ1_^xyjN-?7d$r{p) z_7lzP2)*v(WR!7HoOUUXbFQ0|c44ouBO9XugfvMI@*%&jP6+)X7Wkw>-WY@aEkskn z`8N3G&1^mSZo50HZ&!x~v^#KU0>?WmD4!ZXvgkLYr3YE)a!c>)GThNnQ6N2?ZqfCS z_Vni}xNe_UHRunKPv}GTGyN|VRz88Uy!IyDyJf@kys1KMTHI3b+k_CFg1N6??i4Ja WfaNpCh8_5R`e5e6%pah6QRE*#a}HGi diff --git a/pos_financial_surcharge/models/__pycache__/res_company.cpython-312.pyc b/pos_financial_surcharge/models/__pycache__/res_company.cpython-312.pyc deleted file mode 100644 index db61b90ef1f0c387f977bcf7defb0d05c5e210cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 834 zcmZuvy^9k;6o0du&Fw|wHm9JZNV_80Qqbc#w9v+YNn^GdhMh^W$9_4pizZwV;o$BH zK?}*hpyW@n$zdbYC}?A)kP{9oXEuqs!hw17d*8qJn2+^(4b*9i2g9H0{ic=mu~uNQ zr@#~_P!vIod=wkLfxv)AK#e1yCNmDI>!i&)Gnd$}j4H16Lo2Q!O_GJO8mE*+BG?22 ztfFDDudK(a9|Ipz@C}N5lT{8%o|h8al%}Z{6e3S!k26&&%u^mR;pwUw8O1EgOWY3=k^~`%He|fzidCM#tI1p0 zi0dtmo14|N{!4R9MTv$I7ZdpI?3}(hbMCzFeRA&3??0X2>zp~Ax!qYPKe)EEF-}O# zFqSopRo5cY_$tQF3lgn7HV%^^<6+MAh|(&_m1JanHGaUN2xAxVx{45b<@@}al3ahS zQdG2~sl>$*e0Ca>$6qb`xc92JFp$+n3lpl&c?r3*PHDTAtmrkCeYvh6tta~Pl@&k0 rGF11G_U@AW9>1wnO+egI;+KgK`T~vfs)f4f&5dc}ZR0m+T;~1-`K{6W diff --git a/pos_financial_surcharge/models/pos_payment_method.py b/pos_financial_surcharge/models/pos_payment_method.py index 54cb33f..d967586 100644 --- a/pos_financial_surcharge/models/pos_payment_method.py +++ b/pos_financial_surcharge/models/pos_payment_method.py @@ -1,12 +1,29 @@ import logging -from odoo import models +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError _logger = logging.getLogger(__name__) - class PosPaymentMethod(models.Model): _inherit = 'pos.payment.method' def _get_payment_terminal_selection(self): return super()._get_payment_terminal_selection() + [('financial_surcharge', 'Card financial surcharge')] + + bank_charge_prod_id = fields.Many2one( + 'product.product', + domain=[('type', '=', 'service'), ('available_in_pos', '=', True)], + string="Financial Surcharge Product" + ) + + @api.onchange('bank_charge_prod_id') + def _onchange_bank_charge_prod_id(self): + if self.bank_charge_prod_id and self.bank_charge_prod_id.taxes_id: + raise ValidationError(_("Add a product without taxes.")) + + @api.model + def _load_pos_data_fields(self, config_id): + params = super()._load_pos_data_fields(config_id) + params += ['bank_charge_prod_id'] + return params diff --git a/pos_financial_surcharge/models/pos_session.py b/pos_financial_surcharge/models/pos_session.py index 46f9b1b..dd4ca31 100644 --- a/pos_financial_surcharge/models/pos_session.py +++ b/pos_financial_surcharge/models/pos_session.py @@ -7,4 +7,4 @@ class PosSession(models.Model): @api.model def _load_pos_data_models(self, config_id): - return super()._load_pos_data_models(config_id) + ['account.card', 'account.card.installment'] + return super()._load_pos_data_models(config_id) + ['account.card', 'account.card.installment','product.product','account.tax',] diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge.js b/pos_financial_surcharge/static/src/app/financial_surcharge.js index 49993e0..83fcf7b 100644 --- a/pos_financial_surcharge/static/src/app/financial_surcharge.js +++ b/pos_financial_surcharge/static/src/app/financial_surcharge.js @@ -29,7 +29,6 @@ export class FinancialSurcharge extends PaymentInterface { }), }) } - debugger; return res; } async send_payment_request(cid) { @@ -38,14 +37,15 @@ export class FinancialSurcharge extends PaymentInterface { try { // During payment creation, user can't cancel the payment intent line.set_payment_status("waitingCapture"); + // Call Mercado Pago to create a payment intent return await ask( this.env.services.dialog, { - title: 'Select payment intallment ', + title: 'Select paym', line: line, cards: await this._get_cards(), - order: line.pos_order_id, - pos: this.pos, + order: this.pos.get_order(), + pos: this.pos, }, {}, FinancialSurchargePopup diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js index 730a331..0e6bd99 100644 --- a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js +++ b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js @@ -11,38 +11,87 @@ export class FinancialSurchargePopup extends ConfirmationDialog { cards: Object, pos: Object, }; + async _confirm() { - debugger; - const instalments = this.props.line.models['account.card.installment'].getAllBy('id') - this.props.line.amount=this.state.raw_amount * instalments[this.state.selected_installment].surcharge_coefficient; - const diff_amount = this.state.raw_amount - this.props.line.amount - debugger; - await this.props.pos.addLineToCurrentOrder({ - product_id: 26,// filo crea un data que te cree un producto recargo haber como lo podes relacionar - qty: 1, - price: diff_amount, - note: instalments[this.state.selected_installment].name, - }); + const instalments = this.props.line.models['account.card.installment'].getAllBy('id'); + const selected_id = this.state.selected_installment; + const installment = instalments[selected_id]; + + + if (!installment) { + console.warn("⚠️ Plan de cuotas no encontrado."); + return this.execButton(this.props.confirm); + } + + const surcharge_coefficient = installment.surcharge_coefficient || 1.0; + const raw_amount = this.state.raw_amount; + const total_with_surcharge = raw_amount * surcharge_coefficient; + const diff_amount = total_with_surcharge - raw_amount; + + this.props.line.amount = total_with_surcharge; + + const pos_payment_method = this.props.line.payment_method_id?.raw; + + if (surcharge_coefficient > 1.0 && diff_amount > 0.0) { + if (pos_payment_method && pos_payment_method.bank_charge_prod_id) { + const product = this.props.pos.models['product.product'].getBy('id', pos_payment_method.bank_charge_prod_id); + if (product) { + const tax = this.props.pos.models['account.tax'].getBy('id', 1); + if (tax && (!product.taxes_id || product.taxes_id.length === 0)) { + product.taxes_id = [tax.id]; + } + + console.log("🔍 PRODUCTO CARGADO:"); + console.log("ID:", product.id); + console.log("Nombre:", product.display_name); + console.log("Taxes ID:", product.taxes_id); + console.log("Tipo:", product.type); + this.props.pos.addLineToCurrentOrder({ + product_id: product, + price_unit: parseFloat(diff_amount.toFixed(2)), + }, {}); + + const order = this.props.pos.get_order(); + const orderLines = order.get_orderlines(); + const lastLine = orderLines[orderLines.length - 1]; + + const card_name = installment.card_id?.name || "Tarjeta desconocida"; + const installment_name = installment.name; + lastLine.set_customer_note(`Tarjeta: ${card_name} | Cuotas: ${installment_name}`); + } else { + console.warn("⚠️ Producto de recargo no encontrado."); + } + } else { + console.warn("⚠️ No se encontró el método de pago o el producto."); + } + } + + console.log("💳 Nuevo monto del pago:", this.props.line.amount); this.props.line.set_payment_status("done"); return this.execButton(this.props.confirm); } + + + static defaultProps = { ...ConfirmationDialog.defaultProps, confirmLabel: _t("Confirm Payment"), cancelLabel: _t("Cancel Payment"), title: _t("Card register"), }; + formatCurrency(amount) { return this.env.utils.formatCurrency(amount); } + setup() { super.setup(); this.props.body = _t(" %s", this.props.title); this.amount = this.env.utils.formatCurrency(this.props.line.amount); this.raw_amount = this.props.line.amount; this.cards = this.props.cards; - this.state = useState({raw_amount : this.props.line.amount}); + this.state = useState({ raw_amount: this.props.line.amount }); } -} +} \ No newline at end of file diff --git a/pos_financial_surcharge/views/financial_surcharge_payment_method_view.xml b/pos_financial_surcharge/views/financial_surcharge_payment_method_view.xml new file mode 100644 index 0000000..b4da76a --- /dev/null +++ b/pos_financial_surcharge/views/financial_surcharge_payment_method_view.xml @@ -0,0 +1,15 @@ + + + + pos.payment.method.inherit.financial.surcharge + pos.payment.method + + + + + + + + + From 76c4bb7d564d9ea23d20e48848ef1201d2dd6fbe Mon Sep 17 00:00:00 2001 From: fsuleWitdata Date: Fri, 13 Jun 2025 09:36:23 -0300 Subject: [PATCH 3/5] UPG: mejoras funcionales MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mejoras en el flujo de pago con tarjeta de crédito: Se fuerza la selección de una tarjeta y cantidad de cuotas antes de continuar. Antes de seleccionar la tarjeta, se permite ingresar el monto a cobrar, habilitando el uso de múltiples tarjetas. Independientemente de si la cuota tiene recargo o no, se genera automáticamente una nota en la última línea de la orden indicando el medio de pago utilizado. --- .../static/src/app/financial_surcharge.js | 7 +++ .../financial_surcharge_popup.js | 43 ++++++++------ .../financial_surcharge_popup.xml | 58 ++++++++++--------- 3 files changed, 64 insertions(+), 44 deletions(-) diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge.js b/pos_financial_surcharge/static/src/app/financial_surcharge.js index 83fcf7b..e950219 100644 --- a/pos_financial_surcharge/static/src/app/financial_surcharge.js +++ b/pos_financial_surcharge/static/src/app/financial_surcharge.js @@ -34,6 +34,13 @@ export class FinancialSurcharge extends PaymentInterface { async send_payment_request(cid) { await super.send_payment_request(...arguments); const line = this.pos.get_order().get_selected_paymentline(); + + // Validar que haya monto ingresado + const amount = line.amount; + if (!amount || amount <= 0) { + this._showMsg("Debe ingresar un monto antes de seleccionar este método de pago.", "Monto no válido"); + return false; + } try { // During payment creation, user can't cancel the payment intent line.set_payment_status("waitingCapture"); diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js index 0e6bd99..d761658 100644 --- a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js +++ b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js @@ -13,10 +13,15 @@ export class FinancialSurchargePopup extends ConfirmationDialog { }; async _confirm() { - const instalments = this.props.line.models['account.card.installment'].getAllBy('id'); const selected_id = this.state.selected_installment; - const installment = instalments[selected_id]; + if (!selected_id) { + alert("Debe seleccionar un plan de cuotas."); + return; + } + + const instalments = this.props.line.models['account.card.installment'].getAllBy('id'); + const installment = instalments[selected_id]; if (!installment) { console.warn("⚠️ Plan de cuotas no encontrado."); @@ -31,6 +36,12 @@ export class FinancialSurchargePopup extends ConfirmationDialog { this.props.line.amount = total_with_surcharge; const pos_payment_method = this.props.line.payment_method_id?.raw; + const order = this.props.pos.get_order(); + + const card_name = installment.card_id?.name || "Tarjeta desconocida"; + const installment_name = installment.name; + + const customer_note = `Tarjeta: ${card_name}\nCuotas: ${installment_name}`; if (surcharge_coefficient > 1.0 && diff_amount > 0.0) { if (pos_payment_method && pos_payment_method.bank_charge_prod_id) { @@ -41,24 +52,10 @@ export class FinancialSurchargePopup extends ConfirmationDialog { product.taxes_id = [tax.id]; } - console.log("🔍 PRODUCTO CARGADO:"); - console.log("ID:", product.id); - console.log("Nombre:", product.display_name); - console.log("Taxes ID:", product.taxes_id); - console.log("Tipo:", product.type); - this.props.pos.addLineToCurrentOrder({ product_id: product, price_unit: parseFloat(diff_amount.toFixed(2)), }, {}); - - const order = this.props.pos.get_order(); - const orderLines = order.get_orderlines(); - const lastLine = orderLines[orderLines.length - 1]; - - const card_name = installment.card_id?.name || "Tarjeta desconocida"; - const installment_name = installment.name; - lastLine.set_customer_note(`Tarjeta: ${card_name} | Cuotas: ${installment_name}`); } else { console.warn("⚠️ Producto de recargo no encontrado."); } @@ -67,6 +64,15 @@ export class FinancialSurchargePopup extends ConfirmationDialog { } } + // Siempre guardar nota, incluso sin recargo + const orderlines = order.get_orderlines(); + const line_to_note = orderlines.at(-1); + if (line_to_note) { + line_to_note.set_customer_note(customer_note); + } else { + console.warn("⚠️ No hay líneas de pedido para asignar la nota."); + } + console.log("💳 Nuevo monto del pago:", this.props.line.amount); this.props.line.set_payment_status("done"); return this.execButton(this.props.confirm); @@ -92,6 +98,9 @@ export class FinancialSurchargePopup extends ConfirmationDialog { this.amount = this.env.utils.formatCurrency(this.props.line.amount); this.raw_amount = this.props.line.amount; this.cards = this.props.cards; - this.state = useState({ raw_amount: this.props.line.amount }); + this.state = useState({ + raw_amount: this.props.line.amount, + selected_installment: "", + }); } } \ No newline at end of file diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.xml b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.xml index f4560b8..1df8524 100644 --- a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.xml +++ b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.xml @@ -1,29 +1,33 @@ - - - -
-

-

- Amount: - -
- -
-
-
- -
+ + +
+

+

+ Amount: +
+ +
+
+
+ \ No newline at end of file From 391d2b4aecb54a3470910f7b899d99b96d1420a7 Mon Sep 17 00:00:00 2001 From: fsuleWitdata Date: Fri, 13 Jun 2025 09:41:58 -0300 Subject: [PATCH 4/5] Create README.md --- pos_financial_surcharge/README.md | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 pos_financial_surcharge/README.md diff --git a/pos_financial_surcharge/README.md b/pos_financial_surcharge/README.md new file mode 100644 index 0000000..c1fb565 --- /dev/null +++ b/pos_financial_surcharge/README.md @@ -0,0 +1,90 @@ +# Módulo: POS - Recargo Financiero por Tarjeta + +Este módulo permite aplicar recargos financieros personalizados según el plan de cuotas elegido por el cliente al pagar con tarjeta en el Punto de Venta (POS) de Odoo 18. Es especialmente útil para operaciones con tarjetas de crédito donde el comercio asume un interés bancario y desea trasladarlo al consumidor. + +## Características Principales + +- Permite asociar productos de recargo a métodos de pago POS. +- Soporte para múltiples tarjetas y planes de cuotas (cuotas, coeficiente de recargo, descuentos bancarios). +- Muestra un popup en el POS para seleccionar tarjeta, lote, cupón y plan de financiación. +- Calcula y agrega automáticamente el recargo como una línea adicional en el pedido. +- Configuración visual por método de pago para definir qué tarjetas están permitidas. +- Validaciones de productos sin impuestos en los recargos. + +--- + +## Instalación + +1. Clonar este módulo en el directorio de addons de tu instancia de Odoo: + ```bash + git clone https://github.com/filoquin/pos_payment.git + ``` + +2. Activar el modo desarrollador en Odoo y habilitar el módulo. + +--- + +## Configuración + +### 1. Productos de Recargo +- Crear un producto tipo "Servicio" con `Disponible en POS` y sin impuestos y para argentina IVA 0% si se factura. +- Este producto se usará para cargar el importe adicional del plan de financiación. + +### 2. Métodos de Pago POS +- Ir a **Punto de Venta → Configuración → Métodos de pago**. +- Seleccionar un método de tipo `Terminal` e integrar con `Card financial surcharge`. +- Asignar: + - **Producto de recargo financiero** + - **Tarjetas permitidas en POS** + +### 3. Tarjetas y Cuotas +- Crear tarjetas en **Contabilidad → Configuración → Tarjetas**. +- Asociar planes de cuotas a cada tarjeta con: + - Cantidad de cuotas + - Coeficiente de recargo + - Descuento bancario (opcional) + +--- + +## Uso en el Punto de Venta + +1. Al seleccionar un método de pago configurado con recargo, se abre automáticamente un popup. +2. El usuario debe seleccionar: + - Tarjeta + - Plan de cuotas + - Ingresar lote y cupón (si aplica) +3. Se calcula el total ajustado y se agrega una línea de recargo si corresponde. +4. Se guarda una nota de cliente con los datos de la operación. + +--- + +## Detalles Técnicos + +- **Modelos Extendidos:** + - `pos.payment.method`: Añade campo `bank_charge_prod_id` y `available_cards_ids`. + - `account.card`: Filtrado en POS según método de pago. + - `account.card.installment`: Cargado como dependencia POS. +- **Frontend:** + - Reemplaza el flujo de pago estándar con un `PaymentInterface` personalizado. + - Incluye popup interactivo con OWL. +- **Integración POS:** + - Declaración con `register_payment_method("financial_surcharge", ...)`. + +--- + +## Compatibilidad + +- Odoo 18 (Tested) +- Compatible con POS Web y POS Touch +- Modo multi-tienda compatible + +--- + +## Créditos + +Desarrollado por: Martín Quinteros (Filoquin), Francisco Sulé. +Especialista funcional y técnico en Odoo para Argentina 🇦🇷 + +--- + + From 42ab9737d1936be33c6c91cf52f488b9c698aac8 Mon Sep 17 00:00:00 2001 From: fsuleWitdata Date: Sat, 14 Jun 2025 21:54:19 -0300 Subject: [PATCH 5/5] =?UTF-8?q?FIX:=20correcci=C3=B3n=20de=20filo=20y=20ar?= =?UTF-8?q?reglo=20de=20nota=20interna,=20y=20que=20funcione=20el=20select?= =?UTF-8?q?=20en=20el=20placeholder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pos_financial_surcharge/README.md | 1 - pos_financial_surcharge/__init__.py | 1 + pos_financial_surcharge/__manifest__.py | 4 +- .../models/account_card.py | 4 +- .../models/pos_payment_method.py | 24 ++-- pos_financial_surcharge/models/pos_session.py | 2 +- .../static/src/app/financial_surcharge.js | 20 ++- .../financial_surcharge_popup.js | 120 ++++++++++-------- ...method_view.xml => pos_payment_method.xml} | 3 +- pos_financial_surcharge/wizards/__init__.py | 1 + .../wizards/res_config_settings.py | 10 ++ .../wizards/res_config_settings_views.xml | 20 +++ 12 files changed, 120 insertions(+), 90 deletions(-) rename pos_financial_surcharge/views/{financial_surcharge_payment_method_view.xml => pos_payment_method.xml} (82%) create mode 100644 pos_financial_surcharge/wizards/__init__.py create mode 100644 pos_financial_surcharge/wizards/res_config_settings.py create mode 100644 pos_financial_surcharge/wizards/res_config_settings_views.xml diff --git a/pos_financial_surcharge/README.md b/pos_financial_surcharge/README.md index c1fb565..3881a98 100644 --- a/pos_financial_surcharge/README.md +++ b/pos_financial_surcharge/README.md @@ -52,7 +52,6 @@ Este módulo permite aplicar recargos financieros personalizados según el plan 2. El usuario debe seleccionar: - Tarjeta - Plan de cuotas - - Ingresar lote y cupón (si aplica) 3. Se calcula el total ajustado y se agrega una línea de recargo si corresponde. 4. Se guarda una nota de cliente con los datos de la operación. diff --git a/pos_financial_surcharge/__init__.py b/pos_financial_surcharge/__init__.py index 0650744..aee8895 100644 --- a/pos_financial_surcharge/__init__.py +++ b/pos_financial_surcharge/__init__.py @@ -1 +1,2 @@ from . import models +from . import wizards diff --git a/pos_financial_surcharge/__manifest__.py b/pos_financial_surcharge/__manifest__.py index 1e86f2b..a210c31 100644 --- a/pos_financial_surcharge/__manifest__.py +++ b/pos_financial_surcharge/__manifest__.py @@ -2,12 +2,12 @@ 'name': 'Pos Financial Surchage', 'version': "18.0.1.0.0", 'category': 'Sales/Point of Sale', - 'developer': 'Martín Quinteros(Filoquin) , Francisco Sulé', 'sequence': 6, 'summary': 'Add pos finanacial surcharge', 'data': [ 'views/card_installment_view.xml', - 'views/financial_surcharge_payment_method_view.xml', + 'views/pos_payment_method.xml', + 'wizards/res_config_settings_views.xml', ], 'depends': ['point_of_sale', 'card_installment'], 'installable': True, diff --git a/pos_financial_surcharge/models/account_card.py b/pos_financial_surcharge/models/account_card.py index 1a0417b..558d72c 100644 --- a/pos_financial_surcharge/models/account_card.py +++ b/pos_financial_surcharge/models/account_card.py @@ -12,8 +12,8 @@ class AccountCard(models.Model): @api.model def _load_pos_data_domain(self, data): - return [] - # return self.env['account.card']._check_company_domain(data['pos.config']['data'][0]['company_id']) + [('available_in_pos', '=', True)] + return self.env['account.card']._check_company_domain(data['pos.config']['data'][0]['company_id']) + #+ [('available_in_pos', '=', True)] @api.model def _load_pos_data_fields(self, config_id): diff --git a/pos_financial_surcharge/models/pos_payment_method.py b/pos_financial_surcharge/models/pos_payment_method.py index d967586..f088d72 100644 --- a/pos_financial_surcharge/models/pos_payment_method.py +++ b/pos_financial_surcharge/models/pos_payment_method.py @@ -1,29 +1,21 @@ import logging -from odoo import models, fields, api, _ -from odoo.exceptions import ValidationError +from odoo import models, fields, api _logger = logging.getLogger(__name__) + class PosPaymentMethod(models.Model): _inherit = 'pos.payment.method' - def _get_payment_terminal_selection(self): - return super()._get_payment_terminal_selection() + [('financial_surcharge', 'Card financial surcharge')] - - bank_charge_prod_id = fields.Many2one( - 'product.product', - domain=[('type', '=', 'service'), ('available_in_pos', '=', True)], - string="Financial Surcharge Product" + available_card_ids = fields.Many2many( + "account.card", string="Cards", ) - @api.onchange('bank_charge_prod_id') - def _onchange_bank_charge_prod_id(self): - if self.bank_charge_prod_id and self.bank_charge_prod_id.taxes_id: - raise ValidationError(_("Add a product without taxes.")) + + def _get_payment_terminal_selection(self): + return super()._get_payment_terminal_selection() + [('financial_surcharge', 'Card financial surcharge')] @api.model def _load_pos_data_fields(self, config_id): - params = super()._load_pos_data_fields(config_id) - params += ['bank_charge_prod_id'] - return params + return super()._load_pos_data_fields(config_id) + ['available_card_ids'] \ No newline at end of file diff --git a/pos_financial_surcharge/models/pos_session.py b/pos_financial_surcharge/models/pos_session.py index dd4ca31..46f9b1b 100644 --- a/pos_financial_surcharge/models/pos_session.py +++ b/pos_financial_surcharge/models/pos_session.py @@ -7,4 +7,4 @@ class PosSession(models.Model): @api.model def _load_pos_data_models(self, config_id): - return super()._load_pos_data_models(config_id) + ['account.card', 'account.card.installment','product.product','account.tax',] + return super()._load_pos_data_models(config_id) + ['account.card', 'account.card.installment'] diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge.js b/pos_financial_surcharge/static/src/app/financial_surcharge.js index e950219..03fa96d 100644 --- a/pos_financial_surcharge/static/src/app/financial_surcharge.js +++ b/pos_financial_surcharge/static/src/app/financial_surcharge.js @@ -11,7 +11,11 @@ export class FinancialSurcharge extends PaymentInterface { async _get_cards(){ const res = [] const installment_ids = this.pos.models['account.card.installment'].getAll() - for (const card of this.pos.models['account.card'].getAll()) { + const available_card_ids = this.payment_method_id.available_card_ids.map((card)=> card.id); + for (const card of this.pos.models['account.card'].getAll().filter((card) =>{ + return available_card_ids.includes(card.id); + }) + ) { res.push({ id: card.id, name: card.name, @@ -34,25 +38,17 @@ export class FinancialSurcharge extends PaymentInterface { async send_payment_request(cid) { await super.send_payment_request(...arguments); const line = this.pos.get_order().get_selected_paymentline(); - - // Validar que haya monto ingresado - const amount = line.amount; - if (!amount || amount <= 0) { - this._showMsg("Debe ingresar un monto antes de seleccionar este método de pago.", "Monto no válido"); - return false; - } try { // During payment creation, user can't cancel the payment intent line.set_payment_status("waitingCapture"); - // Call Mercado Pago to create a payment intent return await ask( this.env.services.dialog, { - title: 'Select paym', + title: 'Select payment intallment ', line: line, cards: await this._get_cards(), - order: this.pos.get_order(), - pos: this.pos, + order: line.pos_order_id, + pos: this.pos, }, {}, FinancialSurchargePopup diff --git a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js index d761658..b71b326 100644 --- a/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js +++ b/pos_financial_surcharge/static/src/app/financial_surcharge_popup/financial_surcharge_popup.js @@ -1,5 +1,6 @@ import { useState } from "@odoo/owl"; -import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; +import { ConfirmationDialog, AlertDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; +import { compute_price_force_price_include } from "@point_of_sale/app/models/utils/tax_utils"; import { _t } from "@web/core/l10n/translation"; export class FinancialSurchargePopup extends ConfirmationDialog { @@ -12,74 +13,77 @@ export class FinancialSurchargePopup extends ConfirmationDialog { pos: Object, }; - async _confirm() { - const selected_id = this.state.selected_installment; + // Función para crear la nota del cliente + _createCustomerNote(installment) { + const card_name = installment.card_id?.name || "Tarjeta desconocida"; + const installment_name = installment.name; + + return `Tarjeta: ${card_name}\nCuotas: ${installment_name}`; + } - if (!selected_id) { - alert("Debe seleccionar un plan de cuotas."); - return; + async _confirm() { + if (!this.state.selected_installment) { + this._showMsg(_t("You must select an installment"), _t("Error")); + return false; } const instalments = this.props.line.models['account.card.installment'].getAllBy('id'); - const installment = instalments[selected_id]; + const surcharge_coefficient = instalments[this.state.selected_installment].surcharge_coefficient; + const diff_amount = (this.state.raw_amount * surcharge_coefficient) - this.state.raw_amount; - if (!installment) { - console.warn("⚠️ Plan de cuotas no encontrado."); + // Si no hay diferencia (es decir, no aplica recargo), finalizar el proceso y agregar la nota + if (!diff_amount) { + this.props.line.set_payment_status("done"); + await this._addNoteToLastLine(); // Asegurarse de agregar la nota al último producto return this.execButton(this.props.confirm); } - const surcharge_coefficient = installment.surcharge_coefficient || 1.0; - const raw_amount = this.state.raw_amount; - const total_with_surcharge = raw_amount * surcharge_coefficient; - const diff_amount = total_with_surcharge - raw_amount; - - this.props.line.amount = total_with_surcharge; - - const pos_payment_method = this.props.line.payment_method_id?.raw; - const order = this.props.pos.get_order(); + const product_surcharge_id = this.props.pos.company.product_surcharge_id; + const new_price = compute_price_force_price_include( + product_surcharge_id.taxes_id, + diff_amount, + product_surcharge_id, + this.props.pos.config._product_default_values, + this.props.pos.company, + this.props.pos.currency, + this.props.pos.models + ); + + const new_line = await this.props.pos.addLineToCurrentOrder({ + product_id: this.props.pos.company.product_surcharge_id.id, + qty: 1, + price_unit: new_price, + note: instalments[this.state.selected_installment].name, + }); - const card_name = installment.card_id?.name || "Tarjeta desconocida"; - const installment_name = installment.name; - - const customer_note = `Tarjeta: ${card_name}\nCuotas: ${installment_name}`; - - if (surcharge_coefficient > 1.0 && diff_amount > 0.0) { - if (pos_payment_method && pos_payment_method.bank_charge_prod_id) { - const product = this.props.pos.models['product.product'].getBy('id', pos_payment_method.bank_charge_prod_id); - if (product) { - const tax = this.props.pos.models['account.tax'].getBy('id', 1); - if (tax && (!product.taxes_id || product.taxes_id.length === 0)) { - product.taxes_id = [tax.id]; - } - - this.props.pos.addLineToCurrentOrder({ - product_id: product, - price_unit: parseFloat(diff_amount.toFixed(2)), - }, {}); - } else { - console.warn("⚠️ Producto de recargo no encontrado."); - } - } else { - console.warn("⚠️ No se encontró el método de pago o el producto."); - } - } + // Actualizamos el monto de la línea + this.props.line.amount = this.state.raw_amount + new_line.price_subtotal_incl; + this.props.line.set_payment_status("done"); - // Siempre guardar nota, incluso sin recargo - const orderlines = order.get_orderlines(); - const line_to_note = orderlines.at(-1); - if (line_to_note) { - line_to_note.set_customer_note(customer_note); - } else { - console.warn("⚠️ No hay líneas de pedido para asignar la nota."); - } + // Agregamos la nota al último producto de la orden + await this._addNoteToLastLine(); // Asegurarse de agregar la nota al último producto - console.log("💳 Nuevo monto del pago:", this.props.line.amount); - this.props.line.set_payment_status("done"); return this.execButton(this.props.confirm); } + // Función para agregar la nota al último producto de la orden + async _addNoteToLastLine() { + // Obtenemos todas las líneas de la orden + const orderlines = this.props.order.get_orderlines(); + const last_line = orderlines.at(-1); // Accedemos a la última línea de la orden + + if (last_line) { + const instalments = this.props.line.models['account.card.installment'].getAllBy('id'); + const installment = instalments[this.state.selected_installment]; + // Creamos la nota del cliente + const customer_note = this._createCustomerNote(installment); + // Asignamos la nota al último producto de la orden + last_line.set_customer_note(customer_note); + + } + } static defaultProps = { ...ConfirmationDialog.defaultProps, @@ -101,6 +105,14 @@ export class FinancialSurchargePopup extends ConfirmationDialog { this.state = useState({ raw_amount: this.props.line.amount, selected_installment: "", + + }); + } + + _showMsg(msg, title) { + this.env.services.dialog.add(AlertDialog, { + title: "Error " + title, + body: msg, }); } -} \ No newline at end of file +} diff --git a/pos_financial_surcharge/views/financial_surcharge_payment_method_view.xml b/pos_financial_surcharge/views/pos_payment_method.xml similarity index 82% rename from pos_financial_surcharge/views/financial_surcharge_payment_method_view.xml rename to pos_financial_surcharge/views/pos_payment_method.xml index b4da76a..8848576 100644 --- a/pos_financial_surcharge/views/financial_surcharge_payment_method_view.xml +++ b/pos_financial_surcharge/views/pos_payment_method.xml @@ -6,8 +6,7 @@ - + diff --git a/pos_financial_surcharge/wizards/__init__.py b/pos_financial_surcharge/wizards/__init__.py new file mode 100644 index 0000000..0deb68c --- /dev/null +++ b/pos_financial_surcharge/wizards/__init__.py @@ -0,0 +1 @@ +from . import res_config_settings diff --git a/pos_financial_surcharge/wizards/res_config_settings.py b/pos_financial_surcharge/wizards/res_config_settings.py new file mode 100644 index 0000000..81a57e1 --- /dev/null +++ b/pos_financial_surcharge/wizards/res_config_settings.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + product_surcharge_id = fields.Many2one( + related="company_id.product_surcharge_id", + readonly=False, + ) diff --git a/pos_financial_surcharge/wizards/res_config_settings_views.xml b/pos_financial_surcharge/wizards/res_config_settings_views.xml new file mode 100644 index 0000000..8b50334 --- /dev/null +++ b/pos_financial_surcharge/wizards/res_config_settings_views.xml @@ -0,0 +1,20 @@ + + + + res.config.settings.form.inherit.account.payment + res.config.settings + + + + +
+
+
+
+
+
+
+
+