From d7652abb5c262ca0fc518ac84ace161e99913b31 Mon Sep 17 00:00:00 2001 From: Sergey Korobkov Date: Tue, 18 Feb 2025 14:19:00 +0300 Subject: [PATCH] kkm_server_plus release --- kkm_server_plus/.DS_Store | Bin 0 -> 6148 bytes kkm_server_plus/__init__.py | 4 + kkm_server_plus/__manifest__.py | 56 ++ kkm_server_plus/controllers/__init__.py | 3 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 209 bytes .../__pycache__/controllers.cpython-310.pyc | Bin 0 -> 2111 bytes kkm_server_plus/controllers/controllers.py | 56 ++ kkm_server_plus/models/__init__.py | 3 + kkm_server_plus/models/barcode.py | 15 + kkm_server_plus/models/pos.py | 64 +++ kkm_server_plus/security/ir.model.access.csv | 2 + kkm_server_plus/static/description/icon.png | Bin 0 -> 16534 bytes kkm_server_plus/static/src/js/All_js.js | 98 ++++ .../src/js/CancelPaymentByPaymentCard.js | 396 ++++++++++++++ kkm_server_plus/static/src/js/Cash.js | 99 ++++ kkm_server_plus/static/src/js/CheckKM.js | 97 ++++ kkm_server_plus/static/src/js/CleanGUID.js | 40 ++ kkm_server_plus/static/src/js/ClosePop.js | 54 ++ kkm_server_plus/static/src/js/Pay.js | 213 ++++++++ .../static/src/js/PayByPaymentCard.js | 482 ++++++++++++++++++ kkm_server_plus/static/src/js/PosModel.js | 42 ++ kkm_server_plus/static/src/js/ReportX.js | 60 +++ .../src/js/ReturnPaymentByPaymentCard.js | 408 +++++++++++++++ .../static/src/js/TerminalReport.js | 48 ++ .../static/src/js/UniversalIDUpdate.js | 85 +++ kkm_server_plus/static/src/js/close.js | 479 +++++++++++++++++ kkm_server_plus/static/src/js/open.js | 95 ++++ kkm_server_plus/static/src/xml/ClosePop.xml | 19 + .../static/src/xml/PayByPaymentCard.xml | 32 ++ kkm_server_plus/static/src/xml/PosIndex.xml | 11 + .../static/src/xml/UniversalIDUpdate.xml | 10 + .../static/src/xml/service_charge.xml | 30 ++ kkm_server_plus/views/pos.xml | 124 +++++ pos_lot_selection/README.rst | 85 +++ pos_lot_selection/__init__.py | 1 + pos_lot_selection/__manifest__.py | 23 + pos_lot_selection/i18n/es.po | 29 ++ pos_lot_selection/i18n/it.po | 29 ++ pos_lot_selection/i18n/pos_lot_selection.pot | 26 + pos_lot_selection/models/__init__.py | 1 + pos_lot_selection/models/stock_lot.py | 30 ++ pos_lot_selection/readme/DESCRIPTION.rst | 1 + pos_lot_selection/readme/USAGE.rst | 9 + pos_lot_selection/static/description/icon.png | Bin 0 -> 4028 bytes .../static/description/index.html | 429 ++++++++++++++++ .../static/src/js/EditListPopup.js | 23 + .../static/src/js/OrderWidget.js | 29 ++ .../static/src/js/ProductScreen.js | 51 ++ pos_lot_selection/static/src/js/models.js | 35 ++ .../static/src/xml/LotSelectorPopup.xml | 32 ++ 50 files changed, 3958 insertions(+) create mode 100644 kkm_server_plus/.DS_Store create mode 100644 kkm_server_plus/__init__.py create mode 100644 kkm_server_plus/__manifest__.py create mode 100644 kkm_server_plus/controllers/__init__.py create mode 100644 kkm_server_plus/controllers/__pycache__/__init__.cpython-310.pyc create mode 100644 kkm_server_plus/controllers/__pycache__/controllers.cpython-310.pyc create mode 100644 kkm_server_plus/controllers/controllers.py create mode 100644 kkm_server_plus/models/__init__.py create mode 100644 kkm_server_plus/models/barcode.py create mode 100644 kkm_server_plus/models/pos.py create mode 100644 kkm_server_plus/security/ir.model.access.csv create mode 100644 kkm_server_plus/static/description/icon.png create mode 100644 kkm_server_plus/static/src/js/All_js.js create mode 100644 kkm_server_plus/static/src/js/CancelPaymentByPaymentCard.js create mode 100644 kkm_server_plus/static/src/js/Cash.js create mode 100644 kkm_server_plus/static/src/js/CheckKM.js create mode 100644 kkm_server_plus/static/src/js/CleanGUID.js create mode 100644 kkm_server_plus/static/src/js/ClosePop.js create mode 100644 kkm_server_plus/static/src/js/Pay.js create mode 100644 kkm_server_plus/static/src/js/PayByPaymentCard.js create mode 100644 kkm_server_plus/static/src/js/PosModel.js create mode 100644 kkm_server_plus/static/src/js/ReportX.js create mode 100644 kkm_server_plus/static/src/js/ReturnPaymentByPaymentCard.js create mode 100644 kkm_server_plus/static/src/js/TerminalReport.js create mode 100644 kkm_server_plus/static/src/js/UniversalIDUpdate.js create mode 100644 kkm_server_plus/static/src/js/close.js create mode 100644 kkm_server_plus/static/src/js/open.js create mode 100644 kkm_server_plus/static/src/xml/ClosePop.xml create mode 100644 kkm_server_plus/static/src/xml/PayByPaymentCard.xml create mode 100644 kkm_server_plus/static/src/xml/PosIndex.xml create mode 100644 kkm_server_plus/static/src/xml/UniversalIDUpdate.xml create mode 100644 kkm_server_plus/static/src/xml/service_charge.xml create mode 100644 kkm_server_plus/views/pos.xml create mode 100755 pos_lot_selection/README.rst create mode 100755 pos_lot_selection/__init__.py create mode 100755 pos_lot_selection/__manifest__.py create mode 100755 pos_lot_selection/i18n/es.po create mode 100755 pos_lot_selection/i18n/it.po create mode 100755 pos_lot_selection/i18n/pos_lot_selection.pot create mode 100755 pos_lot_selection/models/__init__.py create mode 100755 pos_lot_selection/models/stock_lot.py create mode 100755 pos_lot_selection/readme/DESCRIPTION.rst create mode 100755 pos_lot_selection/readme/USAGE.rst create mode 100755 pos_lot_selection/static/description/icon.png create mode 100755 pos_lot_selection/static/description/index.html create mode 100755 pos_lot_selection/static/src/js/EditListPopup.js create mode 100755 pos_lot_selection/static/src/js/OrderWidget.js create mode 100755 pos_lot_selection/static/src/js/ProductScreen.js create mode 100755 pos_lot_selection/static/src/js/models.js create mode 100755 pos_lot_selection/static/src/xml/LotSelectorPopup.xml diff --git a/kkm_server_plus/.DS_Store b/kkm_server_plus/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..27a8133ab0aa09bf96ee16330444d7a720049083 GIT binary patch literal 6148 zcmeHK%}T>S5Z-NTlTd^n6nb3nTCf#qD_%mZFJMFuDzza+gK4%jsX3HF&iX<=iO=KA z?&c6IcoVTRu=~x<&u->}>5yUds zGSOc&;kP&0oJkh48LNK(N3e*aH0$*~d9B{qYg(3LwX6sKS>}E=%~q*5UEbm7TFN*m z^=WVu&GJe6;6f(ZG)iVml@Nt92)VzFl2GQJTqR+ua((S^9NU?+JL~ny=&URH{o$r7 z)}z5`R}6Z?&Bn10kB-l;CNJ?kk#Cwv4xB65H(0_uD3zLC{bdr%=NM(}`e zoeHQ^xp`u6oep+k;#`A;MxD;MS{ddsE0>QKu2u)TP~nWb8mT7+h=D2tbv5bB75Utr)12ntL* literal 0 HcmV?d00001 diff --git a/kkm_server_plus/__init__.py b/kkm_server_plus/__init__.py new file mode 100644 index 0000000..511a0ca --- /dev/null +++ b/kkm_server_plus/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import controllers +from . import models \ No newline at end of file diff --git a/kkm_server_plus/__manifest__.py b/kkm_server_plus/__manifest__.py new file mode 100644 index 0000000..4716e0e --- /dev/null +++ b/kkm_server_plus/__manifest__.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +{ + 'name': "KKM Server Plus+", + "license": "LGPL-3", + 'summary': """ + Интеграция с онлайн кассами через ККМСервер""", + + 'description': """ + Интеграция с онлайн кассами через ККМСервер, проверка маркировки Честный Знак + """, + + 'author': "MK.Lab, devrave", + 'website': "https://inf-centre.ru", + + 'category': 'Uncategorized', + 'version': '16.2024', + + 'depends': ['point_of_sale', 'pos_lot_selection'], + + 'installable': True, + + 'data': [ + 'security/ir.model.access.csv', + 'views/pos.xml', + 'static/src/xml/PosIndex.xml', + ], + + 'assets': { + 'point_of_sale.assets': [ + 'kkm_server_plus/static/src/js/UniversalIDUpdate.js', + #'kkm_server_plus/static/src/js/UniversalIDUpdate.xml', + 'kkm_server_plus/static/src/js/PosModel.js', + 'kkm_server_plus/static/src/js/open.js', + 'kkm_server_plus/static/src/js/close.js', + 'kkm_server_plus/static/src/js/ReportX.js', + 'kkm_server_plus/static/src/js/CleanGUID.js', + 'kkm_server_plus/static/src/xml/service_charge.xml', + 'kkm_server_plus/static/src/js/All_js.js', + 'kkm_server_plus/static/src/js/Cash.js', + 'kkm_server_plus/static/src/js/PayByPaymentCard.js', + 'kkm_server_plus/static/src/js/CancelPaymentByPaymentCard.js', + 'kkm_server_plus/static/src/js/ReturnPaymentByPaymentCard.js', + 'kkm_server_plus/static/src/js/Pay.js', + 'kkm_server_plus/static/src/js/ClosePop.js', + 'kkm_server_plus/static/src/js/TerminalReport.js', + 'kkm_server_plus/static/src/xml/ClosePop.xml', + 'kkm_server_plus/static/src/xml/PayByPaymentCard.xml', + 'kkm_server_plus/static/src/js/CheckKM.js', + ], + }, + + 'qweb': [ + 'static/src/xml/*.xml', + ], + +} diff --git a/kkm_server_plus/controllers/__init__.py b/kkm_server_plus/controllers/__init__.py new file mode 100644 index 0000000..457bae2 --- /dev/null +++ b/kkm_server_plus/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers \ No newline at end of file diff --git a/kkm_server_plus/controllers/__pycache__/__init__.cpython-310.pyc b/kkm_server_plus/controllers/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa7a68dce4159d67addd4933634e039ed5448c6e GIT binary patch literal 209 zcmYjLK?=e!5KN*6Q40M=4>jPqh%XSqo0m|Uh^0xlWYc0Fd7y7aw7;G*coPE zXBU!W%?QJzyzWR}(aa&p9B42l(oA#99KUeRv^cYD@#Yj~eA-md1`pO6kH<_*%*Vwi z^GlK+eNOTQ&PdPz(%geD^HOVQQMxg&dJN$HLDM*e#t+7;&h{v0-BPK#t%FkAZk$$y O1KnG*k56Dcn2Ik#$u6!o*Ux6yPw$d&=0k(o%(-nahm*Bq+g5lS5SMv?cw^)QAPwdN;*XJ8 zW-S{*^EWu15fDXJi=q!tSMVDpm&H!v11L%F8RV5TH+;U4&l&1Hjo440(s23<19mJFwA#MExEN6 zcuRvuKbI|iPUXt7)8SGEjXtpG=1}Xz zS%)V_K*5VrWCx(lg~&zOf-6+tRu2|N+tm-%z3P|h@#xFZR}0l6P<|S1RSy@c`_<#> z$Ljv*R&{T*)e1~;0zSe}Nf~!}c?n&7H`;>lwl&`Z+wJPXXltQ*2u>eVkI>KPHfTSi z3)te;5wF#~R?rX@`n7nrp{OibuAJ*4zbUzzhL}WxuW-TPW?~M^#5}T-$W8$ZD15?` z73Gan%#^c!GZtN`Y>nJC#5u70eYw4wr@WoBJa6;Q3lT5IjOCeZr<;qNQWkl-2VhJk z7ZB*EpOmuQ*)OR>a;v|oW)9cZ3~VG{07lJuLWv2q_jCU6J>MLhzxr&UwhlX4y6Y$} ziZJ0(r2HsKb5i|v>Pwufy3x};o4cGk@ zE8YgZ2B~-%WMyCB8$40&_+H{wG{6d}nT{HUa{)+Vg?Xk=sOLTPO=^38f^7MJQ!;qv zNPv!JQM?Y{PB_+EXzy>q3%zTA7O4GaHky}Wx!S8I++e!hskOGg@$<5PwXwHs5cach z0hFErLyCyg&lz5HNX6+Ph0#UVY-Zjuah$<~wO~96E?Xt^g@idU3vQIv%d)`Hy(`WD zAkHFt5gEQ0?*a>K9re&3A35qV9dTuWJLQhii7@TQALo-i4vdoWjyh_=oT4}m>P~8)*uuZBdK$S{K)SBdxL6e!MS%q^u#=QG{(qCoyUE4+ zfR|m}3913jTvvv81FdmOF(1>7kGw=o9`Eh^4_ak{(S-#dY(*?DdNFzUUQH5 L=Bzn8durw%^p`D! literal 0 HcmV?d00001 diff --git a/kkm_server_plus/controllers/controllers.py b/kkm_server_plus/controllers/controllers.py new file mode 100644 index 0000000..28e5129 --- /dev/null +++ b/kkm_server_plus/controllers/controllers.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +from odoo import http +from odoo.http import request +import logging +import math +from datetime import date,datetime,timedelta +import requests +import werkzeug +import json + +_logger = logging.getLogger(__name__) + +class OrderInfoController(http.Controller): + @http.route('/order/get_order_info', methods=['POST'], type='json', auth='public') + def get_order_info(self, order_id, **kwargs): + if order_id: + order = request.env['pos.order'].sudo().browse(order_id) + if order: + return {'state': 'success', + 'name': order.pos_reference, + 'UniversalID': order.UniversalID, + 'pay_kkm_guid': order.pay_kkm_guid, + 'pay_cancel_kkm_guid': order.pay_cancel_kkm_guid, + 'pay_return_kkm_guid': order.pay_return_kkm_guid, + } + else: + return {'state': 'error', 'message': 'Не удалось найти возврат.'} + else: + return {'state': 'error', 'message': 'Отсутствует идентификатор возврата.'} + +class LoggerJSMessage(http.Controller): + @http.route('/loggerjs/message', methods=['POST'], type='json', auth='public') + def get_order_info(self, Level, Message, **kwargs): + if Level=='warning': + _logger.warning(Message) + elif Level=='info': + #_logger.info(Message) + _logger.warning(Message) + elif Level=='error': + _logger.error(Message) + else: + _logger.warning(Message) + return Message + +class CashierInfo(http.Controller): + @http.route('/cashier/info', methods=['POST'], type='json', auth='public') + def get_order_info(self, cashier_id, **kwargs): + res = {} + if cashier_id: + cashier = request.env['res.users'].sudo().browse(cashier_id) + if cashier: + res = { + 'name': cashier.name, + 'inn':cashier.inn + } + return res \ No newline at end of file diff --git a/kkm_server_plus/models/__init__.py b/kkm_server_plus/models/__init__.py new file mode 100644 index 0000000..cc89676 --- /dev/null +++ b/kkm_server_plus/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import pos diff --git a/kkm_server_plus/models/barcode.py b/kkm_server_plus/models/barcode.py new file mode 100644 index 0000000..38571d0 --- /dev/null +++ b/kkm_server_plus/models/barcode.py @@ -0,0 +1,15 @@ +import json + +from odoo import fields, models + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + barcode_json = fields.Char( + "Barcode", readonly=True, compute="_compute_barcode_json" + ) + + def _compute_barcode_json(self): + for b in self: + barcode_json = [x for x in b.mapped('product.barcode') if x] + b.barcode_json = json.dumps(barcode_json) \ No newline at end of file diff --git a/kkm_server_plus/models/pos.py b/kkm_server_plus/models/pos.py new file mode 100644 index 0000000..9d75539 --- /dev/null +++ b/kkm_server_plus/models/pos.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +from odoo import api, fields, models +import base64, requests, json + +class User(models.Model): + _inherit = 'res.users' + + inn = fields.Char(string='ИНН') + +class PosOrderNew(models.Model): + _inherit = 'pos.order' + + UniversalID = fields.Char(string='Банковский идентификатор транзакции', readonly=True) + UniversalIDRefund = fields.Char(string='Банковский идентификатор транзакции связанной продажи (для возврата)', readonly=True) + pay_kkm_guid = fields.Char(string='GUID платежа по банковской карте', readonly=True) + pay_cancel_kkm_guid = fields.Char(string='GUID отмены платежа по банковской карте', readonly=True) + pay_return_kkm_guid = fields.Char(string='GUID возврата платежа по банковской карте', readonly=True) + + def _order_fields(self, ui_order): + result = super()._order_fields(ui_order) + result['UniversalID'] = ui_order.get('UniversalID') + result['UniversalIDRefund'] = ui_order.get('UniversalIDRefund') + result['pay_kkm_guid'] = ui_order.get('pay_kkm_guid') + result['pay_cancel_kkm_guid'] = ui_order.get('pay_cancel_kkm_guid') + result['pay_return_kkm_guid'] = ui_order.get('pay_return_kkm_guid') + return result + +class PosConfigNew(models.Model): + _inherit = 'pos.config' + + enable_kkm_server = fields.Boolean(string='KKM Server') + UrlServer = fields.Char(string='UrlServer', default="http://localhost:5893") + User = fields.Char(string='Логин', default="Admin") + Password = fields.Char(string='Пароль', default="1111") + InnKkm_fiscal = fields.Char(string='ИНН фискального регистратора') + InnKkm_term = fields.Char(string='ИНН эквайрингового терминала') + NumDevice_fiscal = fields.Integer(string='Номер фискального регистратора', default=0) + NumDevice_term = fields.Integer(string='Номер эквайрингового терминала', default=0) + NotPrint = fields.Boolean(string='Не печатать на бумаге', default=False) + IsBarCode = fields.Boolean(string='Печатaть штрих-код', default=False) + XRep = fields.Boolean(string='Печатать Х-отчет', default=True) + DoubleReceipt = fields.Boolean(string='Печатать два чека', default=False) + ZRep = fields.Boolean(string='Печатать Z-отчет', default=True) + CashierName = fields.Many2one('res.users', 'Кассир', index=True, required=True, default=lambda self: self.env.user) + CashierVATIN = fields.Char(string='ИНН кассира', related='CashierName.inn') + Detailed = fields.Boolean(string='Полный отчет итогов по картам', default=True) + UseTerminal = fields.Boolean(string='Использовать терминал', default=True) + timeout_term = fields.Integer(string='Ожидание терминала, мин', default=3) + + def suggest(self): + for s in self: + url = s.UrlServer + headers = { "Authorization": "Basic " + base64.b64encode(s.User + ":" + s.Password) } + data = { + "Command": "List", + "NumDevice": 0, + "InnKkm": "", + "Active": True, + "OnOff": True, + "OFD_Error": False, + } + r = requests.post(url, data=json.dumps(data), headers=headers) + return r.json() diff --git a/kkm_server_plus/security/ir.model.access.csv b/kkm_server_plus/security/ir.model.access.csv new file mode 100644 index 0000000..67f6ff2 --- /dev/null +++ b/kkm_server_plus/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_kkm_server_user,kkm_server.user,base.model_res_users,base.group_user,1,0,0,0 diff --git a/kkm_server_plus/static/description/icon.png b/kkm_server_plus/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..49ade6890955dcf1fc9c90eb77f205963f22c855 GIT binary patch literal 16534 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 literal 0 HcmV?d00001 diff --git a/kkm_server_plus/static/src/js/All_js.js b/kkm_server_plus/static/src/js/All_js.js new file mode 100644 index 0000000..629c334 --- /dev/null +++ b/kkm_server_plus/static/src/js/All_js.js @@ -0,0 +1,98 @@ +var glSelf; +var CashierName; +var CashierVATIN; +odoo.define('kkm_server.PaymentScreen', function(require) { + "use strict"; + + const { _t } = require('web.core'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Registries = require('point_of_sale.Registries'); + const NumberBuffer = require('point_of_sale.NumberBuffer'); + + const PosKKMPaymentScreen = (PaymentScreen) => class extends PaymentScreen { + setup() { + super.setup(); + glSelf = this; + this.UrlServer = this.env.pos.config.UrlServer; + if (this.UrlServer=="http://localhost:5893"){this.UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + this.User = this.env.pos.config.User; + this.Password = this.env.pos.config.Password; + this.InnKkm_fiscal=""; + if (this.env.pos.config.InnKkm_fiscal!=undefined && this.env.pos.config.InnKkm_fiscal!=false){ + this.InnKkm_fiscal=this.env.pos.config.InnKkm_fiscal; + } + this.InnKkm_term = ""; + if (this.env.pos.config.InnKkm_term!=undefined && this.env.pos.config.InnKkm_term!=false){ + this.InnKkm_term=this.env.pos.config.InnKkm_term; + } + this.NumDevice_fiscal = this.env.pos.config.NumDevice_fiscal; + this.NumDevice_term = this.env.pos.config.NumDevice_term; + this.IsBarCode = this.env.pos.config.IsBarCode; + this.DoubleReceipt = this.env.pos.config.DoubleReceipt; + } + /** + * @override + */ + async validateOrder(isForceValidate) { + console.log('validateOrder'); + LogJS('info','validateOrder') + var enable_kkm_server = this.env.pos.config.enable_kkm_server + var order = this.env.pos.get_order(); + var quantities = _.map(order.get_orderlines(), function (line) {return line.quantity; }); + var plus=0; + var minus = 0; + var type=0; + var NumDevice = 0 + if (this.env.pos.config.NumDevice_fiscal!=undefined && this.env.pos.config.NumDevice_fiscal!=false){ + NumDevice=this.env.pos.config.NumDevice_fiscal + } + var InnKkm = '' + if (this.env.pos.config.InnKkm_fiscal!=undefined && this.env.pos.config.InnKkm_fiscal!=false){ + InnKkm=this.env.pos.config.InnKkm_fiscal + } + var paymentlines = order.get_paymentlines(); + var Amount = 0; + for (var i = 0; i < paymentlines.length; i++) { + if (paymentlines[i].payment_method.type=="bank") {Amount = Amount + Math.abs(paymentlines[i].amount);} + }; + + if (enable_kkm_server){ + for (var i = 0; i < quantities.length; i++) { + if (quantities[i]>0){plus=plus+1;} + if (quantities[i]<0){minus=minus+1;} + if (plus>0 && minus==0){type=0;} + if (plus==0 && minus>0){type=1;} + if (plus>0 && minus>0){type=99;} + } + console.log('type',type); + LogJS('info','type: '+type) + if (type==99){ + alert("Ошибка! Нельзя в одном чеке делать приход и возврат."); + return; + } + else { + var res = this.env.services.rpc({ + route: '/cashier/info', + params: { + cashier_id: this.env.pos.get_cashier_user_id(), + }, + }, { shadow: true }).then((value) => { CashierName = value['name']; + CashierVATIN = value['inn']; + RegisterCheck(NumDevice, type, this.IsBarCode,this.UrlServer,this.User,this.Password,order,CashierName,CashierVATIN,InnKkm,this.DoubleReceipt) + }).catch((err) => { + CashierName = this.env.pos.config.CashierName[1]; + CashierVATIN = this.env.pos.config.CashierVATIN; + RegisterCheck(NumDevice, type, this.IsBarCode,this.UrlServer,this.User,this.Password,order,CashierName,CashierVATIN,InnKkm,this.DoubleReceipt) + }); + //RegisterCheck(NumDevice, type, this.IsBarCode,this.UrlServer,this.User,this.Password,order,this.CashierName,this.CashierVATIN,InnKkm) + } + } + await super.validateOrder(isForceValidate); + } + + }; + + Registries.Component.extend(PaymentScreen, PosKKMPaymentScreen); + + return PaymentScreen; +}); diff --git a/kkm_server_plus/static/src/js/CancelPaymentByPaymentCard.js b/kkm_server_plus/static/src/js/CancelPaymentByPaymentCard.js new file mode 100644 index 0000000..778dcb5 --- /dev/null +++ b/kkm_server_plus/static/src/js/CancelPaymentByPaymentCard.js @@ -0,0 +1,396 @@ +var CounGetRezult; +var IdCommand; +var UrlServer; +var Password; +var User; +var self; +var ord; +var timeout_term; +var glSelf; + +odoo.define('pos_button.cancel_payment_by_payment_card_kkm', function(require) { +'use strict'; + const { Gui } = require('point_of_sale.Gui'); + const PosComponent = require('point_of_sale.PosComponent'); + const { identifyError } = require('point_of_sale.utils'); + const ProductScreen = require('point_of_sale.ProductScreen'); + const { useListener } = require("@web/core/utils/hooks"); + const Registries = require('point_of_sale.Registries'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Chrome = require('point_of_sale.Chrome'); + + const KKM_Cancel_Payment_By_Payment_Card = (PaymentScreen) => + class extends PaymentScreen { + + KKM_Cancel_Payment_By_Payment_Card_click() { + self = this; + glSelf = this; + console.log('KKM_Cancel_Payment_By_Payment_Card_click'); + LogJS('info','KKM_Cancel_Payment_By_Payment_Card_click') + UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + User = this.env.pos.config.User; + Password = this.env.pos.config.Password; + var NumDevice = 0; + if (this.env.pos.config.NumDevice_term!=undefined && this.env.pos.config.NumDevice_term!=false){ + NumDevice=this.env.pos.config.NumDevice_term; + } + timeout_term = this.env.pos.config.timeout_term; + if (timeout_term<1){ + timeout_term=1 + } + + var order = this.env.pos.get_order(); + ord = order; + var InnKkm = ""; + if (this.env.pos.config.InnKkm_term!=undefined && this.env.pos.config.InnKkm_term!=false){ + InnKkm=this.env.pos.config.InnKkm_term + } + var paymentlines = order.get_paymentlines(); + var Amount = 0; + for (var i = 0; i < paymentlines.length; i++) { + if (paymentlines[i].payment_method.type=="bank") {Amount = Amount + Math.abs(paymentlines[i].amount);} + }; + if (order.orderlines.length && order.orderlines[0].refunded_orderline_id) { + //alert(this.env.pos.toRefundLines) + var refundDetail = this.env.pos.toRefundLines[order.orderlines[0].refunded_orderline_id]; + var refund_id = refundDetail.orderline.orderBackendId + this.env.services.rpc({ + route: '/order/get_order_info', + params: { + order_id: refund_id, + }, + }, { shadow: true }).then((value) => { + var UniversalID = '' + if (value.UniversalID!=undefined && value.UniversalID!=false){ + UniversalID = value.UniversalID + } + else{ + alert('Не удалось получить идентификатор банковской транзакции из заказа.') + return + } + order.set_kkm_UniversalIDRefund(UniversalID) + CancelPaymentByPaymentCard(UrlServer,User,Password,InnKkm,Amount,NumDevice,UniversalID,order); + }).catch((err) => { + alert('Произошла ошибка при попытке получить информацию о возврате.' + err); + return; + }); + } + else{ + alert('Не выбран заказ для отмены.'); + } + + + } + }; + + Registries.Component.extend(PaymentScreen, KKM_Cancel_Payment_By_Payment_Card); + return KKM_Cancel_Payment_By_Payment_Card; +}); + +function CancelPaymentByPaymentCard(UrlServer,User,Password,InnKkm,Amount,NumDevice,UniversalID,order) { + console.log('CancelPaymentByPaymentCard'); + LogJS('info','CancelPaymentByPaymentCard') + var IdCommandOnly = guid() + console.log('guid отмены платежа: ',IdCommandOnly); + LogJS('warning','guid отмены платежа: '+IdCommandOnly) + order.set_kkm_pay_cancel_kkm_guid(IdCommandOnly) + var Data = { + Command: "CancelPaymentByPaymentCard", + InnKkm: InnKkm, + NumDevice: NumDevice, + Amount: Amount, + UniversalID: UniversalID, + IdCommand: IdCommandOnly + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommandCancel(UrlServer,User,Password,Data,self,ExecuteSuccessPayCancelOnly); + //ExecuteCommand(UrlServer,User,Password,Data); +}; + +function ExecuteSuccessPayCancelOnly(Rezult, textStatus, jqXHR) { + console.log('ExecuteSuccessPayCancelOnly'); + LogJS('info','ExecuteSuccessPayCancelOnly') + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + var state = Rezult.Status; + if (state == 1 || state == 4) { // значит команда еще выполняется или еще не запустилась + alert('Терминал находится в режиме ожидания. Проверьте статус возврата платежа позже.'); + } else { + if (Rezult.Status==0){ + if (Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Slip) + //var test = " ИП Свистунова А.Н. \r\n 353560, Краснодарский край, \r\n Славянский р-н, г \r\n Славянск-на-Кубани, \r\n Красная/Дзержинского, -, - \r\nЧЕК: 0012 ОПЛАТА ПОКУПКИ\r\n15.11.23 16:54:31\r\nТЕРМИНАЛ: 92556988\r\nКАРТА: MIR W\r\n ** 0494\r\nAID: A0000006581010 TSI: 4800\r\nTVR: 8080008000 CDA: ----\r\nСУММА (RUB) 41.00\r\n ОДОБРЕНО \r\nКОД ОТВЕТА: 000\r\nКОД АВТОРИЗАЦИИ: 4952NE\r\nССЫЛКА: 331959317247 KVR: ----\r\n================================\r\n\r" + //alert(test); + var res_short = Rezult.Slip.toLowerCase(); //test.toLowerCase() + console.log('res_short: ',res_short); + LogJS('warning','res_short: '+res_short) + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')) + res_short = res_short.slice(0,res_short.indexOf('\n')) + res_short = res_short.replace('код ответа','').replace(':','').trim() + if (res_short=='000'){ + console.log('validateOrder start'); + LogJS('info','validateOrder start') + //alert('УСПЕХ'); + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты.') + return; + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + console.log('validateOrder start'); + LogJS('info','validateOrder start') + //alert('УСПЕХ'); + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + } + else{ + ord.set_kkm_pay_cancel_kkm_guid(null) + alert('Работа терминала завершилась с ошибкой. ' + Rezult.Error); + } + } + }; + +function ExecuteCommandCancel(UrlServer,User,Password, + Data, + self, + FunSuccess, + FunError, + timeout) { + console.log('ExecuteCommandCancel'); + LogJS('info','ExecuteCommandCancel') + if (FunSuccess === undefined) { + FunSuccess = ExecuteSuccess; + } + if (timeout === undefined) { + timeout = timeout_term * 60000; + } + try { + if (KkmServer != undefined) { + if (typeof (Data) == "string") Data = JSON.parse(Data); + KkmServer.Execute(FunSuccess, Data); + return; + }; + } catch { }; + + var JSon = JSON.stringify(Data); + $.support.cors = true; + var jqXHRvar = $.ajax({ + type: 'POST', + async: true, + timeout: timeout, + url: UrlServer + ((UrlServer == "") ? window.location.protocol + "//" + window.location.host + "/" : "/") + 'Execute', + crossDomain: true, + dataType: 'json', + contentType: 'application/json; charset=UTF-8', + processData: false, + data: JSon, + headers: (User != "" || Password != "") ? { "Authorization": "Basic " + btoa(User + ":" + Password) } : "", + success: FunSuccess, + error: FunError + }); + console.log('Ответ ККМ: ',jqXHRvar); + LogJS('warning','Ответ ККМ: '+jqXHRvar) +} + +function ExecuteSuccessPayCancel(Rezult, textStatus, jqXHR) { + console.log('ExecuteSuccessPayCancel'); + LogJS('info','ExecuteSuccessPayCancel') + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + if (Rezult.Status == 0) { + MessageStatus = "Успешно"; + } else if (Rezult.Status == 1) { + MessageStatus = "Выполняется"; + } else if (Rezult.Status == 2) { + MessageStatus = "Ошибка!"; + } else if (Rezult.Status == 3) { + MessageStatus = "Данные не найдены!"; + }; + MessageError = Rezult.Error; + + var MessageCheckNumber = Rezult.CheckNumber; + var MessageSessionNumber = Rezult.SessionNumber; + var MessageLineLength = Rezult.LineLength; + var MessageAmount = Rezult.Amount; + + var state = Rezult.Status; + if (state == 1 || state == 4) { // значит команда еще выполняется или еще не запустилась + //Вывод данных что результат еще не выполнен + CounGetRezult = CounGetRezult + 1; + $("#MessageStatus").text("Выполняется: Запрос №:" + CounGetRezult); + var Data = { + Command: "GetRezult", + IdCommand: IdCommand, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + setTimeout(function () { ExecuteCommandSetRezultCancel(UrlServer,User,Password,Data,null,IdCommand, ExecuteSuccessSetRezultCancel, null, false) }, 1000); + } else { + + if (Rezult.Status==0){ + if (Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Slip) + //var test = " ИП Свистунова А.Н. \r\n 353560, Краснодарский край, \r\n Славянский р-н, г \r\n Славянск-на-Кубани, \r\n Красная/Дзержинского, -, - \r\nЧЕК: 0012 ОПЛАТА ПОКУПКИ\r\n15.11.23 16:54:31\r\nТЕРМИНАЛ: 92556988\r\nКАРТА: MIR W\r\n ** 0494\r\nAID: A0000006581010 TSI: 4800\r\nTVR: 8080008000 CDA: ----\r\nСУММА (RUB) 41.00\r\n ОДОБРЕНО \r\nКОД ОТВЕТА: 000\r\nКОД АВТОРИЗАЦИИ: 4952NE\r\nССЫЛКА: 331959317247 KVR: ----\r\n================================\r\n\r" + //alert(test); + var res_short = Rezult.Slip.toLowerCase(); //test.toLowerCase() + console.log('res_short: ',res_short); + LogJS('warning','res_short: '+res_short) + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')) + res_short = res_short.slice(0,res_short.indexOf('\n')) + res_short = res_short.replace('код ответа','').replace(':','').trim() + if (res_short=='000'){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты.') + return; + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + } + if (MessageError !="" && MessageError!=undefined) { + alert(MessageStatus+"\nСообщение об ошибке:"+MessageError); + return; + }//{mes=mes+"\n Сообщение об ошибке:"+MessageError} + } + }; + +function ExecuteSuccessSetRezultCancel(Rezult, textStatus, jqX) { + console.log('ExecuteSuccessSetRezultCancel'); + LogJS('info','ExecuteSuccessSetRezultCancel') + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + var state = Rezult.Status; + if (state == 1 || state == 4) { + CounGetRezult = CounGetRezult + 1; + $("#MessageStatus").text("Выполняется: Запрос №:" + CounGetRezult); + //alert("Выполняется: Запрос №:" + CounGetRezult) + var Data = { + Command: "GetRezult", + IdCommand: IdCommand, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + setTimeout(function () { ExecuteCommandSetRezultCancel(UrlServer,User,Password,Data,null,IdCommand, ExecuteSuccessSetRezultCancel, null, false) }, 1000); + } else { // Rezult.Status <> 1 - значит команда уже выполнена + // Вывод результата выполнения команды + //alert('RezultNew:\n' + JSON.stringify(Rezult)) + //alert('status:\n' + Rezult.Status) + if (state==0){ + if (Rezult.Rezult.UniversalID){ + ord.set_kkm_UniversalID(Rezult.Rezult.UniversalID) + } + //alert('Slip:\n' + Rezult.Rezult.Slip) + if (Rezult.Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Rezult.Slip) + //var test = " ИП Свистунова А.Н. \r\n 353560, Краснодарский край, \r\n Славянский р-н, г \r\n Славянск-на-Кубани, \r\n Красная/Дзержинского, -, - \r\nЧЕК: 0012 ОПЛАТА ПОКУПКИ\r\n15.11.23 16:54:31\r\nТЕРМИНАЛ: 92556988\r\nКАРТА: MIR W\r\n ** 0494\r\nAID: A0000006581010 TSI: 4800\r\nTVR: 8080008000 CDA: ----\r\nСУММА (RUB) 41.00\r\n ОДОБРЕНО \r\nКОД ОТВЕТА: 000\r\nКОД АВТОРИЗАЦИИ: 4952NE\r\nССЫЛКА: 331959317247 KVR: ----\r\n================================\r\n\r" + var res_short = Rezult.Rezult.Slip.toLowerCase(); + console.log('res_short: ',res_short); + LogJS('warning','res_short: '+res_short) + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')) + res_short = res_short.slice(0,res_short.indexOf('\n')) + res_short = res_short.replace('код ответа','').replace(':','').trim() + if (res_short=='000'){ + self.validateOrder(false) + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + alert('Не вернулся слип чек') + } + } + } + } + + +function ExecuteCommandSetRezultCancel(UrlServer,User,Password, + Data, + currentOrder, + IdCommand, + FunSuccess, + FunError, + timeout) { + console.log('ExecuteCommandSetRezultCancel'); + LogJS('info','ExecuteCommandSetRezultCancel') + if (FunSuccess === undefined) { + FunSuccess = ExecuteSuccess; + } + if (timeout === undefined) { + timeout = timeout_term * 60000; + } + try { + if (KkmServer != undefined) { + if (typeof (Data) == "string") Data = JSON.parse(Data); + KkmServer.Execute(FunSuccess, Data); + return; + }; + } catch { }; + var JSon = JSON.stringify(Data); + $.support.cors = true; + var jqXHRvar = $.ajax({ + type: 'POST', + async: true, + timeout: timeout, + url: UrlServer + ((UrlServer == "") ? window.location.protocol + "//" + window.location.host + "/" : "/") + 'Execute', + crossDomain: true, + dataType: 'json', + contentType: 'application/json; charset=UTF-8', + processData: false, + data: JSon, + headers: (User != "" || Password != "") ? { "Authorization": "Basic " + btoa(User + ":" + Password) } : "", + success: FunSuccess, + error: FunError + }); + console.log('Ответ ККМ: ',jqXHRvar); + LogJS('warning','Ответ ККМ:'+jqXHRvar) +} \ No newline at end of file diff --git a/kkm_server_plus/static/src/js/Cash.js b/kkm_server_plus/static/src/js/Cash.js new file mode 100644 index 0000000..e02dd2f --- /dev/null +++ b/kkm_server_plus/static/src/js/Cash.js @@ -0,0 +1,99 @@ +var glSelf; +var CashierName; +var CashierVATIN; +odoo.define('kkm_server.CashButton', function(require) { + "use strict"; + + const { _t } = require('web.core'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Registries = require('point_of_sale.Registries'); + const NumberBuffer = require('point_of_sale.NumberBuffer'); + const CashMovePopup = require('point_of_sale.CashMovePopup'); + const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup'); + const { _lt } = require('@web/core/l10n/translation'); + const { parse } = require('web.field_utils'); + const { useRef, useState } = owl; + + const PosKKMCashButton = (CashMovePopup) => class extends CashMovePopup { + confirm() { + glSelf = this; + console.log('CashButton'); + LogJS('info','CashButton') + var UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + var User = this.env.pos.config.User; + var Password = this.env.pos.config.Password; + var res = super.confirm(); + var Amount = this.state.inputAmount; + var NumDevice = 0 + if (this.env.pos.config.NumDevice_fiscal!=undefined && this.env.pos.config.NumDevice_fiscal!=false){ + NumDevice=this.env.pos.config.NumDevice_fiscal + } + var InnKkm = '' + if (this.env.pos.config.InnKkm_fiscal!=undefined && this.env.pos.config.InnKkm_fiscal!=false){ + InnKkm=this.env.pos.config.InnKkm_fiscal + } + var Command = "" + if (res===undefined){ + return res; + } + if (this.state.inputType === 'out') { + Command = "PaymentCash"; + } + if (this.state.inputType === 'in') { + Command = "DepositingCash"; + } + var res = this.env.services.rpc({ + route: '/cashier/info', + params: { + cashier_id: this.env.pos.get_cashier_user_id(), + }, + }, { shadow: true }).then((value) => { CashierName = value['name']; + CashierVATIN = value['inn']; + if (Command!=""){ + var Data = { + Command: Command, + InnKkm: InnKkm, + NumDevice: NumDevice, + CashierName: CashierName, + CashierVATIN: CashierVATIN, + Amount: Amount, + IdCommand: guid() + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); + } + return res; + }).catch((err) => { + CashierName = this.env.pos.config.CashierName[1]; + CashierVATIN = this.env.pos.config.CashierVATIN; + if (Command!=""){ + var Data = { + Command: Command, + InnKkm: InnKkm, + NumDevice: NumDevice, + CashierName: CashierName, + CashierVATIN: CashierVATIN, + Amount: Amount, + IdCommand: guid() + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); + } + return res; + }); + + } + }; + + CashMovePopup.template = 'point_of_sale.CashMovePopup'; + CashMovePopup.defaultProps = { + cancelText: _lt('Cancel'), + title: _lt('Cash In/Out'), + }; + Registries.Component.extend(CashMovePopup, PosKKMCashButton); + + return CashMovePopup; +}); \ No newline at end of file diff --git a/kkm_server_plus/static/src/js/CheckKM.js b/kkm_server_plus/static/src/js/CheckKM.js new file mode 100644 index 0000000..9f39cda --- /dev/null +++ b/kkm_server_plus/static/src/js/CheckKM.js @@ -0,0 +1,97 @@ +var NumDevice; +var InnKkm; +var TaxVariant; +var KktNumber; +var IdCommand; +var UrlServer; +var Password; +var User; +var self; +var order; +var glSelf; + +odoo.define('kkm_server.check_km_kkm', function(require) { +'use strict'; + + const PosComponent = require('point_of_sale.PosComponent'); + const { identifyError } = require('point_of_sale.utils'); + const ProductScreen = require('point_of_sale.ProductScreen'); + const { useListener } = require("@web/core/utils/hooks"); + const Registries = require('point_of_sale.Registries'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Chrome = require('point_of_sale.Chrome'); + const BarcodeReader = require('point_of_sale.BarcodeReader'); + const { get_lot_lines } = require('point_of_sale.models'); + const {PosGlobalState} = require("point_of_sale.models"); + var core = require('web.core'); + var utils = require('web.utils'); + const PosLotSaleProductScreen = require('pos_lot_selection.ProductScreen'); + +// Проверка кодов маркировки +function ValidationMarkingCode(NumDevice,UrlServer,User,Password,InnKkm,product) { + var Data = { + Command: "ValidationMarkingCode", + NumDevice: NumDevice, + InnKkm: InnKkm, + TaxVariant: "", + KktNumber: "", + "GoodCodeDatas": [{ + "Name": product.display_name, + "Barcode": window.selectedLot, + Quantity: 1, + MeasureOfQuantity: 0, + PackageQuantity: null, + }, + ], + IdCommand: guid(), + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); + +} + +// Barcode scaner + BarcodeReader.include({ + scan: async function (code) { + if (!code) return; + self = this; + glSelf = this; + UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + User = this.env.pos.config.User; + Password = this.env.pos.config.Password; + var NumDevice = this.env.pos.config.NumDevice_term; + var InnKkm = ""; + if (this.env.pos.config.InnKkm_term!=undefined && this.env.pos.config.InnKkm_term!=false){ + InnKkm=this.env.pos.config.InnKkm_term + } + const callbacks = Object.keys(this.exclusive_callbacks).length + ? this.exclusive_callbacks + : this.action_callbacks; + let parsed_result = this.barcode_parser.parse_barcode(code); + const product = this.env.pos.db.get_product_by_barcode(code); + if (Array.isArray(parsed_result)) { + [...callbacks.gs1].map(cb => cb(parsed_result)); + } else { + if (callbacks[parsed_result.type]) { + for (const cb of callbacks[parsed_result.type]) { + await cb(parsed_result); + if (window.lot_error == true) { + return; + } + ValidationMarkingCode(NumDevice,UrlServer,User,Password,InnKkm,product); + return; + } + } else if (callbacks.error) { + [...callbacks.error].map(cb => cb(parsed_result)); + } else { + console.warn(`Ignored Barcode scan:`, parsed_result); + } + } + }, + }); + + return BarcodeReader; + +}); \ No newline at end of file diff --git a/kkm_server_plus/static/src/js/CleanGUID.js b/kkm_server_plus/static/src/js/CleanGUID.js new file mode 100644 index 0000000..36e35b0 --- /dev/null +++ b/kkm_server_plus/static/src/js/CleanGUID.js @@ -0,0 +1,40 @@ +var CounGetRezult; +var IdCommand; +var UrlServer; +var Password; +var User; +var self; +var ord; +var timeout_term; +var glSelf; + +odoo.define('pos_button.clean_guid_kkm', function(require) { +'use strict'; + const { Gui } = require('point_of_sale.Gui'); + const PosComponent = require('point_of_sale.PosComponent'); + const { identifyError } = require('point_of_sale.utils'); + const ProductScreen = require('point_of_sale.ProductScreen'); + const { useListener } = require("@web/core/utils/hooks"); + const Registries = require('point_of_sale.Registries'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Chrome = require('point_of_sale.Chrome'); + + const KKM_Clean_GUID = (PaymentScreen) => + class extends PaymentScreen { + + KKM_Clean_GUID_click() { + self = this; + glSelf = this; + console.log('KKM_Clean_GUID_click'); + LogJS('info','KKM_Clean_GUID_click') + var order = this.env.pos.get_order(); + order.set_kkm_pay_kkm_guid(null) + order.set_kkm_pay_return_kkm_guid(null) + order.set_kkm_pay_cancel_kkm_guid(null) + alert('GUID-ы KKM очищены.') + } + }; + + Registries.Component.extend(PaymentScreen, KKM_Clean_GUID); + return KKM_Clean_GUID; +}); \ No newline at end of file diff --git a/kkm_server_plus/static/src/js/ClosePop.js b/kkm_server_plus/static/src/js/ClosePop.js new file mode 100644 index 0000000..95894ca --- /dev/null +++ b/kkm_server_plus/static/src/js/ClosePop.js @@ -0,0 +1,54 @@ +var glSelf; +odoo.define('kkm_server.ClosePosPopup', function(require) { + "use strict"; + + const { _t } = require('web.core'); + const ClosePosPopup = require('point_of_sale.ClosePosPopup'); + const Registries = require('point_of_sale.Registries'); + const NumberBuffer = require('point_of_sale.NumberBuffer'); + + const PosKKMClosePosPopup = (ClosePosPopup) => class extends ClosePosPopup { + + /** + * @override + */ + async closeSession() { + glSelf = this; + console.log('ClosePosPopup'); + LogJS('info','ClosePosPopup') + var enable_kkm_server = this.env.pos.config.enable_kkm_server + var UseTerminal = this.env.pos.config.UseTerminal + if (enable_kkm_server && UseTerminal){ + var self = this; + var UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + var User = this.env.pos.config.User; + var Password = this.env.pos.config.Password; + var NumDevice = 0; + if (this.env.pos.config.NumDevice_term!=undefined && this.env.pos.config.NumDevice_term!=false){ + NumDevice=this.env.pos.config.NumDevice_term + } + var InnKkm = ""; + if (this.env.pos.config.InnKkm_term!=undefined && this.env.pos.config.InnKkm_term!=false){ + InnKkm=this.env.pos.config.InnKkm_term + } + var Data = { + Command: "Settlement", + InnKkm: InnKkm, + NumDevice: NumDevice, + IdCommand: guid() + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); + } + await super.closeSession(); + } + + + }; + + Registries.Component.extend(ClosePosPopup, PosKKMClosePosPopup); + + return PosKKMClosePosPopup; +}); diff --git a/kkm_server_plus/static/src/js/Pay.js b/kkm_server_plus/static/src/js/Pay.js new file mode 100644 index 0000000..543e58f --- /dev/null +++ b/kkm_server_plus/static/src/js/Pay.js @@ -0,0 +1,213 @@ +var glSelf; +var self; + +odoo.define('pos_button.pay_all_kkm', function(require) { +'use strict'; + const { Gui } = require('point_of_sale.Gui'); + const PosComponent = require('point_of_sale.PosComponent'); + const { identifyError } = require('point_of_sale.utils'); + const ProductScreen = require('point_of_sale.ProductScreen'); + const { useListener } = require("@web/core/utils/hooks"); + const Registries = require('point_of_sale.Registries'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Chrome = require('point_of_sale.Chrome'); + + const KKM_Pay_All = (PaymentScreen) => + class extends PaymentScreen { + + KKM_pay_all_click() { + self = this; + glSelf = this; + console.log('KKM_pay_all_click'); + LogJS('info','KKM_pay_all_click') + var order = this.env.pos.get_order(); + var enable_kkm_server = this.env.pos.config.enable_kkm_server + var UseTerminal = this.env.pos.config.UseTerminal + var quantities = _.map(order.get_orderlines(), function (line) {return line.quantity; }); + var plus=0; + var minus = 0; + var type=0; + var Amount = 0; + var paymentlines = order.get_paymentlines(); + for (var i = 0; i < paymentlines.length; i++) { + if (paymentlines[i].payment_method.type=="bank") {Amount = Amount + Math.abs(paymentlines[i].amount);} + }; + if (enable_kkm_server && UseTerminal){ + for (var i = 0; i < quantities.length; i++) { + if (quantities[i]>0){plus=plus+1;} + if (quantities[i]<0){minus=minus+1;} + if (plus>0 && minus==0){type=0;} + if (plus==0 && minus>0){type=1;} + if (plus>0 && minus>0){type=99;} + } + if (type==99){ + alert("Ошибка! Нельзя в одном чеке делать приход и возврат."); + return; + } + if (type==1 & Amount>0) { + console.log('Возврат платежа по карте'); + LogJS('info','Возврат платежа по карте') + if (order.pay_return_kkm_guid!=undefined && order.pay_return_kkm_guid!=false){ + console.log('Возврат платежа'); + LogJS('info','Возврат платежа') + var Data = { + Command: "GetRezult", + IdCommand: order.pay_return_kkm_guid, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommandSetRezult(UrlServer,User,Password,Data,null,order.pay_return_kkm_guid, ExecuteSuccessSetRezultOnly, null, false) + } + else{ + if (order.pay_cancel_kkm_guid!=undefined && order.pay_cancel_kkm_guid!=false){ + console.log('Отмена платежа'); + LogJS('info','Отмена платежа') + var Data = { + Command: "GetRezult", + IdCommand: order.pay_cancel_kkm_guid, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommandSetRezult(UrlServer,User,Password,Data,null,order.pay_cancel_kkm_guid, ExecuteSuccessSetRezultOnly, null, false) + } + else{ + alert('Отсутствует guid операции возврата платежа на банковскую карту. Выполните возврат на банковскую карту.') + } + } + } + else{ + if (type==0 & Amount>0){ + console.log('Платеж по карте'); + LogJS('info','Платеж по карте') + if (order.pay_kkm_guid!=undefined && order.pay_kkm_guid!=false){ + var Data = { + Command: "GetRezult", + IdCommand: order.pay_kkm_guid, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommandSetRezult(UrlServer,User,Password,Data,null,order.pay_kkm_guid, ExecuteSuccessSetRezultOnly, null, false) + } + else{ + this.KKM_Pay_By_Payment_Card_click() + //alert('tut') + //alert('Отсутствует guid операции платежа по банковской карте. Выполните платеж банковской картой.') + } + } + else{ + console.log('Платеж/возврат наличными'); + LogJS('info','Платеж/возврат наличными') + this.validateOrder(false) + } + } + } + else{ + console.log('Операция без применения ккм'); + LogJS('info','Операция без применения ккм') + this.validateOrder(false) + } + } + }; + +// KKM_pay_all_click() { +// var self = this; +// var order = this.env.pos.get_order(); +// var enable_kkm_server = this.env.pos.config.enable_kkm_server +// var quantities = _.map(order.get_orderlines(), function (line) {return line.quantity; }); +// var plus=0; +// var minus = 0; +// var type=0; +// var Amount = 0; +// var paymentlines = order.get_paymentlines(); +// for (var i = 0; i < paymentlines.length; i++) { +// if (paymentlines[i].payment_method.type=="bank") {Amount = Amount + Math.abs(paymentlines[i].amount);} +// }; +// if (enable_kkm_server){ +// for (var i = 0; i < quantities.length; i++) { +// if (quantities[i]>0){plus=plus+1;} +// if (quantities[i]<0){minus=minus+1;} +// if (plus>0 && minus==0){type=0;} +// if (plus==0 && minus>0){type=1;} +// if (plus>0 && minus>0){type=99;} +// } +// if (type==99){ +// alert("Ошибка! Нельзя в одном чеке делать приход и возврат."); +// return; +// } +// if (type==1 & Amount>0) { +// this.KKM_Return_Payment_By_Payment_Card_click() +// } +// else{ +// if (type==0 & Amount>0){ +// this.KKM_Pay_By_Payment_Card_click() +// } +// else{ +// this.validateOrder(false) +// } +// } +// } +// else{ +// this.validateOrder(false) +// } +// } +// }; + + Registries.Component.extend(PaymentScreen, KKM_Pay_All); + return KKM_Pay_All; +}); + +function ExecuteSuccessSetRezultOnly(Rezult, textStatus, jqX) { + console.log('ExecuteSuccessSetRezultOnly'); + LogJS('info','ExecuteSuccessSetRezultOnly') + var state = Rezult.Status; + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + if (state == 1 || state == 4) { + alert('Терминал еще ожидает платеж. Проверьте статус платежа позже'); + } else { + if (state==0){ + if (Rezult.Rezult.UniversalID){ + ord.set_kkm_UniversalID(Rezult.Rezult.UniversalID) + } + if (Rezult.Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Rezult.Slip) + var res_short = Rezult.Rezult.Slip.toLowerCase(); + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')) + res_short = res_short.slice(0,res_short.indexOf('\n')) + res_short = res_short.replace('код ответа','').replace(':','').trim() + if (res_short=='000' || res_short=='r00'){ + self.validateOrder(false) + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + alert('Не вернулся слип чек') + } + } + else{ + alert('Работа терминала завершилась с ошибкой'); + } + } + } \ No newline at end of file diff --git a/kkm_server_plus/static/src/js/PayByPaymentCard.js b/kkm_server_plus/static/src/js/PayByPaymentCard.js new file mode 100644 index 0000000..e998f7f --- /dev/null +++ b/kkm_server_plus/static/src/js/PayByPaymentCard.js @@ -0,0 +1,482 @@ +var glSelf; +var CounGetRezult; +var IdCommand; +var UrlServer; +var Password; +var User; +var self; +var ord; +var timeout_term; +var CashierName; +var CashierVATIN; + +odoo.define('pos_button.pay_by_payment_card_kkm', function(require) { +'use strict'; + const { Gui } = require('point_of_sale.Gui'); + const PosComponent = require('point_of_sale.PosComponent'); + const { identifyError } = require('point_of_sale.utils'); + const ProductScreen = require('point_of_sale.ProductScreen'); + const { useListener } = require("@web/core/utils/hooks"); + const Registries = require('point_of_sale.Registries'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Chrome = require('point_of_sale.Chrome'); + + const KKM_Pay_By_Payment_Card = (PaymentScreen) => + class extends PaymentScreen { + + KKM_Pay_By_Payment_Card_click() { + self = this; + glSelf = this; + console.log('KKM_Pay_By_Payment_Card_click'); + LogJS('info','KKM_Pay_By_Payment_Card_click') + UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + User = this.env.pos.config.User; + Password = this.env.pos.config.Password; + var NumDevice = 0; + if (this.env.pos.config.NumDevice_term!=undefined && this.env.pos.config.NumDevice_term!=false){ + NumDevice=this.env.pos.config.NumDevice_term + } + var InnKkm = ""; + if (this.env.pos.config.InnKkm_term!=undefined && this.env.pos.config.InnKkm_term!=false){ + InnKkm=this.env.pos.config.InnKkm_term + } + var order = this.env.pos.get_order(); + var paymentlines = order.get_paymentlines(); + var IsBarCode = this.env.pos.config.IsBarCode; + timeout_term = this.env.pos.config.timeout_term; + if (timeout_term<1){ + timeout_term=1 + } + var NumDevice_kkt = 0; + if (this.env.pos.config.NumDevice_fiscal!=undefined && this.env.pos.config.NumDevice_fiscal!=false){ + NumDevice_kkt=this.env.pos.config.NumDevice_fiscal + } + var InnKkm_kkt = '' + if (this.env.pos.config.InnKkm_fiscal!=undefined && this.env.pos.config.InnKkm_fiscal!=false){ + InnKkm_kkt=this.env.pos.config.InnKkm_fiscal + } + var Amount = 0; + for (var i = 0; i < paymentlines.length; i++) { + if (paymentlines[i].payment_method.type=="bank") {Amount = Amount + Math.abs(paymentlines[i].amount);} + }; + if (Amount==0){ + alert('Не указана сумма для оплаты по безналичному расчету'); + return; + } + var currentOrder = this.currentOrder + var quantities = _.map(currentOrder.get_orderlines(), function (line) {return line.quantity; }); + var plus=0; + var minus = 0; + var type=0; + for (var i = 0; i < quantities.length; i++) { + if (quantities[i]>0){plus=plus+1;} + if (quantities[i]<0){minus=minus+1;} + if (plus>0 && minus==0){type=0;} + if (plus==0 && minus>0){type=1;} + if (plus>0 && minus>0){type=99;} + } + if (type!=0){ + alert('Сумма платежей должна быть положительной!') + return; + } + ord = this.currentOrder + var res = this.env.services.rpc({ + route: '/cashier/info', + params: { + cashier_id: this.env.pos.get_cashier_user_id(), + }, + }, { shadow: true }).then((value) => { CashierName = value['name']; + CashierVATIN = value['inn']; + PayByPaymentCard(UrlServer,User,Password,InnKkm,Amount,NumDevice,currentOrder,self,IsBarCode,CashierName,CashierVATIN,NumDevice_kkt,InnKkm_kkt); + }).catch((err) => { + CashierName = this.env.pos.config.CashierName[1]; + CashierVATIN = this.env.pos.config.CashierVATIN; + PayByPaymentCard(UrlServer,User,Password,InnKkm,Amount,NumDevice,currentOrder,self,IsBarCode,CashierName,CashierVATIN,NumDevice_kkt,InnKkm_kkt); + }); + + } + }; + + Registries.Component.extend(PaymentScreen, KKM_Pay_By_Payment_Card); + return KKM_Pay_By_Payment_Card; +}); + + +function PayByPaymentCard(UrlServer,User,Password,InnKkm,Amount,NumDevice,currentOrder,self,IsBarCode,CashierName,CashierVATIN,NumDevice_kkt,InnKkm_kkt) { + console.log('PayByPaymentCard'); + LogJS('info','PayByPaymentCard') + var IdCommandOnly = guid() + console.log('guid платежа: ',IdCommandOnly); + LogJS('warning','guid платежа: '+IdCommandOnly) + currentOrder.set_kkm_pay_kkm_guid(IdCommandOnly) + var Data = { + Command: "PayByPaymentCard", + InnKkm: InnKkm, + NumDevice: NumDevice, + Amount: Amount, + IdCommand: IdCommandOnly, + ReceiptNumber: currentOrder.name, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommandPay(UrlServer,User,Password,Data,currentOrder,self,IsBarCode,CashierName,CashierVATIN,NumDevice_kkt,InnKkm_kkt,IdCommandOnly,ExecuteSuccessPayOnly); +} + +function ExecuteSuccessPayOnly(Rezult, textStatus, jqX) { + console.log('ExecuteSuccessPayOnly'); + LogJS('info','ExecuteSuccessPayOnly') + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + var state = Rezult.Status; + if (state==0){ + if (Rezult.UniversalID){ + ord.set_kkm_UniversalID(Rezult.UniversalID) + } + if (Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Slip) + //var test = " ИП Свистунова А.Н. \r\n 353560, Краснодарский край, \r\n Славянский р-н, г \r\n Славянск-на-Кубани, \r\n Красная/Дзержинского, -, - \r\nЧЕК: 0012 ОПЛАТА ПОКУПКИ\r\n15.11.23 16:54:31\r\nТЕРМИНАЛ: 92556988\r\nКАРТА: MIR W\r\n ** 0494\r\nAID: A0000006581010 TSI: 4800\r\nTVR: 8080008000 CDA: ----\r\nСУММА (RUB) 41.00\r\n ОДОБРЕНО \r\nКОД ОТВЕТА: 000\r\nКОД АВТОРИЗАЦИИ: 4952NE\r\nССЫЛКА: 331959317247 KVR: ----\r\n================================\r\n\r" + //alert(test) + var res_short = Rezult.Slip.toLowerCase(); //test.toLowerCase() // + //alert('res_short:\n' + res_short) + console.log('res_short: ',res_short); + LogJS('warning','res_short: '+res_short) + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')); + res_short = res_short.slice(0,res_short.indexOf('\n')); + res_short = res_short.replace('код ответа','').replace(':','').trim(); + //alert(res_short) + if (res_short=='000'){ + //alert('УСПЕХ') + console.log('validateOrder start'); + LogJS('info','validateOrder start') + self.validateOrder(false); + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + //alert('УСПЕХ'); + console.log('validateOrder start'); + LogJS('info','validateOrder start') + self.validateOrder(false); + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты'); + return; + } + } + } + else{ + alert('Не вернулся слип чек'); + } + } + else{ + if (state == 1 || state == 4){ + if (Rezult.Slip!=undefined){ + console.log('Slip (статус ожидания. Слип от QR): ',Rezult.Slip); + LogJS('warning','Slip (статус ожидания. Слип от QR): '+Rezult.Slip) + var res_short = Rezult.Slip.toLowerCase(); //test.toLowerCase() // + console.log('res_short: ',res_short); + LogJS('warning','res_short: '+res_short) + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')); + res_short = res_short.slice(0,res_short.indexOf('\n')); + res_short = res_short.replace('код ответа','').replace(':','').trim(); + //alert(res_short) + if (res_short=='000' || res_short=='r00'){ + console.log('validateOrder start'); + LogJS('info','validateOrder start') + self.validateOrder(false); + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + console.log('validateOrder start'); + LogJS('info','validateOrder start') + self.validateOrder(false); + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты'); + return; + } + } + } + else{ + alert('Терминал еще ожидает платеж. Проверьте статус платежа позже.'); + } + } + else{ + //ord.set_kkm_pay_kkm_guid(null) + alert('Работа терминала завершилась с ошибкой. ' + Rezult.Error); + } + } +}; + +//---------------------------------------------------------------------------------------- +// Пример асинхронного запроса для интерактивного ввода данных на сервере +// Рекомендуется как основной способ работы с эквайринговыми терминалами (или с другим оборудованием с интерактивным вводом данных) + +// Оплатить платежной картой +function PayByPaymentCardAsync(UrlServer,User,Password,InnKkm,Amount,NumDevice,currentOrder,self,IsBarCode,CashierName,CashierVATIN,NumDevice_kkt,InnKkm_kkt) { + console.log('PayByPaymentCardAsync'); + LogJS('info','PayByPaymentCardAsync') + CounGetRezult = 0; + IdCommand = guid() + var Data = { + Command: "PayByPaymentCard", + InnKkm: InnKkm, + NumDevice: NumDevice, + Amount: Amount, + IdCommand: IdCommand, + CardNumber: "", + ReceiptNumber: currentOrder.name, + Timeout: 1, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommandPay(UrlServer,User,Password,Data,currentOrder,self,IsBarCode,CashierName,CashierVATIN,NumDevice_kkt,InnKkm_kkt,IdCommand,ExecuteSuccessPay); +} + +function ExecuteCommandPay(UrlServer,User,Password, + Data, + currentOrder, + self, + IsBarCode, + CashierName, + CashierVATIN, + NumDevice_kkt, + InnKkm_kkt, + IdCommand, + FunSuccess, + FunError, + timeout) { + console.log('ExecuteCommandPay'); + LogJS('info','ExecuteCommandPay') + if (FunSuccess === undefined) { + FunSuccess = ExecuteSuccess; + } + if (timeout === undefined) { + timeout = timeout_term * 60000; + } + try { + if (KkmServer != undefined) { + if (typeof (Data) == "string") Data = JSON.parse(Data); + KkmServer.Execute(FunSuccess, Data); + return; + }; + } catch { }; + var JSon = JSON.stringify(Data); + $.support.cors = true; + var jqXHRvar = $.ajax({ + type: 'POST', + async: true, + timeout: timeout, + url: UrlServer + ((UrlServer == "") ? window.location.protocol + "//" + window.location.host + "/" : "/") + 'Execute', + crossDomain: true, + dataType: 'json', + contentType: 'application/json; charset=UTF-8', + processData: false, + data: JSon, + headers: (User != "" || Password != "") ? { "Authorization": "Basic " + btoa(User + ":" + Password) } : "", + success: FunSuccess, + error: FunError + }); + console.log('Ответ ККМ: ',jqXHRvar); + LogJS('warning','Ответ ККМ: '+jqXHRvar) +} + +function ExecuteCommandSetRezult(UrlServer,User,Password, + Data, + currentOrder, + IdCommand, + FunSuccess, + FunError, + timeout) { + console.log('ExecuteCommandSetRezult'); + LogJS('info','ExecuteCommandSetRezult') + if (FunSuccess === undefined) { + FunSuccess = ExecuteSuccess; + } + if (timeout === undefined) { + timeout = timeout_term * 60000; + } + try { + if (KkmServer != undefined) { + if (typeof (Data) == "string") Data = JSON.parse(Data); + KkmServer.Execute(FunSuccess, Data); + return; + }; + } catch { }; + var JSon = JSON.stringify(Data); + $.support.cors = true; + var jqXHRvar = $.ajax({ + type: 'POST', + async: true, + timeout: timeout, + url: UrlServer + ((UrlServer == "") ? window.location.protocol + "//" + window.location.host + "/" : "/") + 'Execute', + crossDomain: true, + dataType: 'json', + contentType: 'application/json; charset=UTF-8', + processData: false, + data: JSon, + headers: (User != "" || Password != "") ? { "Authorization": "Basic " + btoa(User + ":" + Password) } : "", + success: FunSuccess, + error: FunError + }); + console.log('Ответ ККМ: ',jqXHRvar); + LogJS('warning','Ответ ККМ: '+jqXHRvar) +} + +function ExecuteSuccessSetRezult(Rezult, textStatus, jqX) { + console.log('ExecuteSuccessSetRezult'); + LogJS('info','ExecuteSuccessSetRezult') + var state = Rezult.Status; + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + if (state == 1 || state == 4) { + CounGetRezult = CounGetRezult + 1; + $("#MessageStatus").text("Выполняется: Запрос №:" + CounGetRezult); + console.log('Выполняется запрос №: ',CounGetRezult); + LogJS('warning','Выполняется запрос №:: '+CounGetRezult) + //alert("Выполняется: Запрос №:" + CounGetRezult) + var Data = { + Command: "GetRezult", + IdCommand: IdCommand, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + setTimeout(function () { ExecuteCommandSetRezult(UrlServer,User,Password,Data,null,IdCommand, ExecuteSuccessSetRezult, null, false) }, 1000); + } else { // Rezult.Status <> 1 - значит команда уже выполнена + // Вывод результата выполнения команды + //alert('RezultNew:\n' + JSON.stringify(Rezult)) + //alert('status:\n' + Rezult.Status) + if (state==0){ + if (Rezult.Rezult.UniversalID){ + ord.set_kkm_UniversalID(Rezult.Rezult.UniversalID) + } + //alert('Slip:\n' + Rezult.Rezult.Slip) + + if (Rezult.Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Rezult.Slip) + //var test = " ИП Свистунова А.Н. \r\n 353560, Краснодарский край, \r\n Славянский р-н, г \r\n Славянск-на-Кубани, \r\n Красная/Дзержинского, -, - \r\nЧЕК: 0012 ОПЛАТА ПОКУПКИ\r\n15.11.23 16:54:31\r\nТЕРМИНАЛ: 92556988\r\nКАРТА: MIR W\r\n ** 0494\r\nAID: A0000006581010 TSI: 4800\r\nTVR: 8080008000 CDA: ----\r\nСУММА (RUB) 41.00\r\n ОДОБРЕНО \r\nКОД ОТВЕТА: 000\r\nКОД АВТОРИЗАЦИИ: 4952NE\r\nССЫЛКА: 331959317247 KVR: ----\r\n================================\r\n\r" + var res_short = Rezult.Rezult.Slip.toLowerCase(); + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')) + res_short = res_short.slice(0,res_short.indexOf('\n')) + res_short = res_short.replace('код ответа','').replace(':','').trim() + if (res_short=='000'){ + self.validateOrder(false) + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + alert('Не вернулся слип чек') + } + } + } + } + +function ExecuteSuccessPay(Rezult, textStatus, jqX) { + console.log('ExecuteSuccessPay'); + LogJS('info','ExecuteSuccessPay') + //alert(Rezult.Status) + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + var state = Rezult.Status; + if (state == 1 || state == 4) { // значит команда еще выполняется или еще не запустилась + //Вывод данных что результат еще не выполнен + CounGetRezult = CounGetRezult + 1; + $("#MessageStatus").text("Выполняется: Запрос №:" + CounGetRezult); + var Data = { + Command: "GetRezult", + IdCommand: IdCommand, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + setTimeout(function () { ExecuteCommandSetRezult(UrlServer,User,Password,Data,null,IdCommand, ExecuteSuccessSetRezult, null, false) }, 1000); + } else { // Rezult.Status <> 1 - значит команда уже выполнена + // Вывод результата выполнения команды + //alert('RezultNew:\n' + JSON.stringify(Rezult)) + //alert('status:\n' + Rezult.Status) + if (state==0){ + if (Rezult.UniversalID){ + ord.set_kkm_UniversalID(Rezult.UniversalID) + } + if (Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Slip) + //var test = " ИП Свистунова А.Н. \r\n 353560, Краснодарский край, \r\n Славянский р-н, г \r\n Славянск-на-Кубани, \r\n Красная/Дзержинского, -, - \r\nЧЕК: 0012 ОПЛАТА ПОКУПКИ\r\n15.11.23 16:54:31\r\nТЕРМИНАЛ: 92556988\r\nКАРТА: MIR W\r\n ** 0494\r\nAID: A0000006581010 TSI: 4800\r\nTVR: 8080008000 CDA: ----\r\nСУММА (RUB) 41.00\r\n ОДОБРЕНО \r\nКОД ОТВЕТА: 000\r\nКОД АВТОРИЗАЦИИ: 4952NE\r\nССЫЛКА: 331959317247 KVR: ----\r\n================================\r\n\r" + //alert(test) + var res_short = Rezult.Slip.toLowerCase(); //test.toLowerCase() // + //alert('res_short:\n' + res_short) + console.log('res_short: ',res_short); + LogJS('warning','res_short: '+res_short) + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')) + res_short = res_short.slice(0,res_short.indexOf('\n')) + res_short = res_short.replace('код ответа','').replace(':','').trim() + //alert(res_short) + if (res_short=='000'){ + self.validateOrder(false) + //RegisterCheck(NumDevice_kkt, 0, IsBarCode,UrlServer,User,Password,currentOrder,CashierName,CashierVATIN,InnKkm_kkt) + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + alert('Не вернулся слип чек') + } + } + } + } diff --git a/kkm_server_plus/static/src/js/PosModel.js b/kkm_server_plus/static/src/js/PosModel.js new file mode 100644 index 0000000..b1b8c87 --- /dev/null +++ b/kkm_server_plus/static/src/js/PosModel.js @@ -0,0 +1,42 @@ + odoo.define('kkm_server_plus.PosModel', function(require) { + 'use strict'; + + const { Orderline } = require('point_of_sale.models'); + const Registries = require("point_of_sale.Registries"); + var utils = require('web.utils'); + + var round_di = utils.round_decimals; + + const CustomOrderline = (OriginalOrderline) => + class extends OriginalOrderline { + can_be_merged_with(orderline){ + var price = parseFloat(round_di(this.price || 0, this.pos.dp['Product Price']).toFixed(this.pos.dp['Product Price'])); + var order_line_price = orderline.get_product().get_price(orderline.order.pricelist, this.get_quantity()); + order_line_price = round_di(orderline.compute_fixed_price(order_line_price), this.pos.currency.decimal_places); + if( this.get_product().id !== orderline.get_product().id){ //only orderline of the same product can be merged + return false; + }else if(!this.get_unit() || !this.get_unit().is_pos_groupable){ + return false; + }else if(this.get_discount() > 0){ // we don't merge discounted orderlines + return false; + }else if(!utils.float_is_zero(price - order_line_price - orderline.get_price_extra(), + this.pos.currency.decimal_places)){ + return false; + // добавил сюда серийники + }else if(this.product.tracking == 'lot' || this.product.tracking == 'serial' && (this.pos.picking_type.use_create_lots || this.pos.picking_type.use_existing_lots)) { + return false; + }else if (this.description !== orderline.description) { + return false; + }else if (orderline.get_customer_note() !== this.get_customer_note()) { + return false; + } else if (this.refunded_orderline_id) { + return false; + }else{ + return true; + } + } + }; + + Registries.Model.extend(Orderline, CustomOrderline); + return Orderline; + }); \ No newline at end of file diff --git a/kkm_server_plus/static/src/js/ReportX.js b/kkm_server_plus/static/src/js/ReportX.js new file mode 100644 index 0000000..c8d273a --- /dev/null +++ b/kkm_server_plus/static/src/js/ReportX.js @@ -0,0 +1,60 @@ +var mes=""; +var glSelf; + +odoo.define('kkm_server.reportx_kkm', function(require) { +'use strict'; + const { Gui } = require('point_of_sale.Gui'); + const PosComponent = require('point_of_sale.PosComponent'); + const { identifyError } = require('point_of_sale.utils'); + const ProductScreen = require('point_of_sale.ProductScreen'); + const { useListener } = require("@web/core/utils/hooks"); + const Registries = require('point_of_sale.Registries'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + +class KKM_ReportX extends PosComponent { + setup() { + super.setup(); + useListener('click', this.onClick); + } + async onClick() { + var self = this; + glSelf = this; + console.log('reportx_kkm'); + LogJS('info','reportx_kkm') + var UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + var User = this.env.pos.config.User; + var Password = this.env.pos.config.Password; + var NumDevice = 0; + if (this.env.pos.config.NumDevice_fiscal!=undefined && this.env.pos.config.NumDevice_fiscal!=false){ + NumDevice=this.env.pos.config.NumDevice_fiscal + } + var InnKkm = '' + if (this.env.pos.config.InnKkm_fiscal!=undefined && this.env.pos.config.InnKkm_fiscal!=false){ + InnKkm=this.env.pos.config.InnKkm_fiscal + } + XReport(NumDevice,UrlServer,User,Password,InnKkm); + } + } + + KKM_ReportX.template = 'KKM_ReportX'; + ProductScreen.addControlButton({ + component: KKM_ReportX, + condition: function () { + var has_enabled = false; + if (this.env.pos.config.enable_kkm_server) { + has_enabled = true; + } + return has_enabled; + }, + position: ['before', 'RefundButton'], + }); + + Registries.Component.add(KKM_ReportX); + + return KKM_ReportX; +}); + + + + diff --git a/kkm_server_plus/static/src/js/ReturnPaymentByPaymentCard.js b/kkm_server_plus/static/src/js/ReturnPaymentByPaymentCard.js new file mode 100644 index 0000000..6ff46f8 --- /dev/null +++ b/kkm_server_plus/static/src/js/ReturnPaymentByPaymentCard.js @@ -0,0 +1,408 @@ +var CounGetRezult; +var IdCommand; +var UrlServer; +var Password; +var User; +var self; +var ord; +var timeout_term; +var glSelf; + +odoo.define('pos_button.return_payment_by_payment_card_kkm', function(require) { +'use strict'; + const { Gui } = require('point_of_sale.Gui'); + const PosComponent = require('point_of_sale.PosComponent'); + const { identifyError } = require('point_of_sale.utils'); + const ProductScreen = require('point_of_sale.ProductScreen'); + const { useListener } = require("@web/core/utils/hooks"); + const Registries = require('point_of_sale.Registries'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Chrome = require('point_of_sale.Chrome'); + + const KKM_Return_Payment_By_Payment_Card = (PaymentScreen) => + class extends PaymentScreen { + + KKM_Return_Payment_By_Payment_Card_click() { + self = this; + glSelf = this; + console.log('KKM_Return_Payment_By_Payment_Card_click'); + LogJS('info','KKM_Return_Payment_By_Payment_Card_click') + UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + User = this.env.pos.config.User; + Password = this.env.pos.config.Password; + var NumDevice = this.env.pos.config.NumDevice_term; + var InnKkm = ""; + if (this.env.pos.config.InnKkm_term!=undefined && this.env.pos.config.InnKkm_term!=false){ + InnKkm=this.env.pos.config.InnKkm_term + } + timeout_term = this.env.pos.config.timeout_term; + if (timeout_term<1){ + timeout_term=1 + } + var order = this.env.pos.get_order(); + ord = order; + var paymentlines = order.get_paymentlines(); + var Amount = 0; + for (var i = 0; i < paymentlines.length; i++) { + if (paymentlines[i].payment_method.type=="bank") {Amount = Amount + Math.abs(paymentlines[i].amount);} + }; + if (Amount==0){ + alert('Не указана сумма для возврата по безналичному расчету'); + return; + } + var quantities = _.map(order.get_orderlines(), function (line) {return line.quantity; }); + var plus=0; + var minus = 0; + var type=0; + for (var i = 0; i < quantities.length; i++) { + if (quantities[i]>0){plus=plus+1;} + if (quantities[i]<0){minus=minus+1;} + if (plus>0 && minus==0){type=0;} + if (plus==0 && minus>0){type=1;} + if (plus>0 && minus>0){type=99;} + } + if (type!=1){ + alert('Сумма платежей должна быть отрицательной!') + return; + } + if (order.orderlines.length && order.orderlines[0].refunded_orderline_id) { + //alert(this.env.pos.toRefundLines) + var refundDetail = this.env.pos.toRefundLines[order.orderlines[0].refunded_orderline_id]; + var refund_id = refundDetail.orderline.orderBackendId + this.env.services.rpc({ + route: '/order/get_order_info', + params: { + order_id: refund_id, + }, + }, { shadow: true }).then((value) => { + var UniversalID = '' + if (value.UniversalID!=undefined && value.UniversalID!=false){ + UniversalID = value.UniversalID + } + else{ + alert('Не удалось получить идентификатор банковской транзакции из заказа.') + return + } + order.set_kkm_UniversalIDRefund(UniversalID) + ReturnPaymentByPaymentCard(UrlServer,User,Password,InnKkm,Amount,NumDevice,UniversalID,self); + }).catch((err) => { + alert('Произошла ошибка при попытке получить информацию о возврате.' + err); + return; + }); + } + else{ + alert('Не выбран заказ для возврата.'); + } + } + }; + + Registries.Component.extend(PaymentScreen, KKM_Return_Payment_By_Payment_Card); + return KKM_Return_Payment_By_Payment_Card; +}); + +function ReturnPaymentByPaymentCard(UrlServer,User,Password,InnKkm,Amount,NumDevice,UniversalID,self) { + console.log('ReturnPaymentByPaymentCard'); + LogJS('info','ReturnPaymentByPaymentCard') + var IdCommandOnly = guid() + console.log('guid платежа: ',IdCommandOnly); + LogJS('warning','guid платежа: '+IdCommandOnly) + ord.set_kkm_pay_return_kkm_guid(IdCommandOnly) + var Data = { + Command: "ReturnPaymentByPaymentCard", + InnKkm: InnKkm, + NumDevice: NumDevice, + Amount: Amount, + UniversalID: UniversalID, + IdCommand: IdCommandOnly, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommandReturn(UrlServer,User,Password,Data,self,ExecuteSuccessPayReturnOnly); +}; + +function ExecuteSuccessPayReturnOnly(Rezult, textStatus, jqXHR) { + console.log('ExecuteSuccessPayReturnOnly'); + LogJS('info','ExecuteSuccessPayReturnOnly') + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + var state = Rezult.Status; + if (state == 1 || state == 4) { // значит команда еще выполняется или еще не запустилась + alert('Терминал находится в режиме ожидания. Проверьте статус возврата платежа позже.'); + } else { + if (Rezult.Status==0){ + if (Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Slip) + //var test = " ИП Свистунова А.Н. \r\n 353560, Краснодарский край, \r\n Славянский р-н, г \r\n Славянск-на-Кубани, \r\n Красная/Дзержинского, -, - \r\nЧЕК: 0012 ОПЛАТА ПОКУПКИ\r\n15.11.23 16:54:31\r\nТЕРМИНАЛ: 92556988\r\nКАРТА: MIR W\r\n ** 0494\r\nAID: A0000006581010 TSI: 4800\r\nTVR: 8080008000 CDA: ----\r\nСУММА (RUB) 41.00\r\n ОДОБРЕНО \r\nКОД ОТВЕТА: 000\r\nКОД АВТОРИЗАЦИИ: 4952NE\r\nССЫЛКА: 331959317247 KVR: ----\r\n================================\r\n\r" + //alert(test); + var res_short = Rezult.Slip.toLowerCase(); //test.toLowerCase() + console.log('res_short: ',res_short); + LogJS('warning','res_short: '+res_short) + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')) + res_short = res_short.slice(0,res_short.indexOf('\n')) + res_short = res_short.replace('код ответа','').replace(':','').trim() + if (res_short=='000'){ + console.log('validateOrder start'); + LogJS('info','validateOrder start') + //alert('УСПЕХ'); + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты.') + return; + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + console.log('validateOrder start'); + LogJS('info','validateOrder start') + //alert('УСПЕХ'); + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + } + else{ + ord.set_kkm_pay_return_kkm_guid(null) + alert('Работа терминала завершилась с ошибкой. ' + Rezult.Error); + } + } + }; + +function ExecuteCommandReturn(UrlServer,User,Password, + Data, + self, + FunSuccess, + FunError, + timeout) { + console.log('ExecuteCommandReturn'); + LogJS('info','ExecuteCommandReturn') + if (FunSuccess === undefined) { + FunSuccess = ExecuteSuccess; + } + if (timeout === undefined) { + timeout = timeout_term * 60000; + } + try { + if (KkmServer != undefined) { + if (typeof (Data) == "string") Data = JSON.parse(Data); + KkmServer.Execute(FunSuccess, Data); + return; + }; + } catch { }; + + var JSon = JSON.stringify(Data); + $.support.cors = true; + var jqXHRvar = $.ajax({ + type: 'POST', + async: true, + timeout: timeout, + url: UrlServer + ((UrlServer == "") ? window.location.protocol + "//" + window.location.host + "/" : "/") + 'Execute', + crossDomain: true, + dataType: 'json', + contentType: 'application/json; charset=UTF-8', + processData: false, + data: JSon, + headers: (User != "" || Password != "") ? { "Authorization": "Basic " + btoa(User + ":" + Password) } : "", + success: FunSuccess, + error: FunError + }); + console.log('Ответ ККМ: ',jqXHRvar); + LogJS('warning','Ответ ККМ: '+jqXHRvar) +} + +function ExecuteSuccessPayReturn(Rezult, textStatus, jqXHR) { + console.log('ExecuteSuccessPayReturn'); + LogJS('info','ExecuteSuccessPayReturn') + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + if (Rezult.Status == 0) { + MessageStatus = "Успешно"; + } else if (Rezult.Status == 1) { + MessageStatus = "Выполняется"; + } else if (Rezult.Status == 2) { + MessageStatus = "Ошибка!"; + } else if (Rezult.Status == 3) { + MessageStatus = "Данные не найдены!"; + }; + MessageError = Rezult.Error; + + var MessageCheckNumber = Rezult.CheckNumber; + var MessageSessionNumber = Rezult.SessionNumber; + var MessageLineLength = Rezult.LineLength; + var MessageAmount = Rezult.Amount; + + var state = Rezult.Status; + if (state == 1 || state == 4) { // значит команда еще выполняется или еще не запустилась + //Вывод данных что результат еще не выполнен + CounGetRezult = CounGetRezult + 1; + $("#MessageStatus").text("Выполняется: Запрос №:" + CounGetRezult); + var Data = { + Command: "GetRezult", + IdCommand: IdCommand, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + setTimeout(function () { ExecuteCommandSetRezultReturn(UrlServer,User,Password,Data,null,IdCommand, ExecuteSuccessSetRezultReturn, null, false) }, 1000); + } else { + + if (Rezult.Status==0){ + if (Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Slip) + //var test = " ИП Свистунова А.Н. \r\n 353560, Краснодарский край, \r\n Славянский р-н, г \r\n Славянск-на-Кубани, \r\n Красная/Дзержинского, -, - \r\nЧЕК: 0012 ОПЛАТА ПОКУПКИ\r\n15.11.23 16:54:31\r\nТЕРМИНАЛ: 92556988\r\nКАРТА: MIR W\r\n ** 0494\r\nAID: A0000006581010 TSI: 4800\r\nTVR: 8080008000 CDA: ----\r\nСУММА (RUB) 41.00\r\n ОДОБРЕНО \r\nКОД ОТВЕТА: 000\r\nКОД АВТОРИЗАЦИИ: 4952NE\r\nССЫЛКА: 331959317247 KVR: ----\r\n================================\r\n\r" + //alert(test); + var res_short = Rezult.Slip.toLowerCase(); //test.toLowerCase() + console.log('res_short: ',res_short); + LogJS('warning','res_short: '+res_short) + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')) + res_short = res_short.slice(0,res_short.indexOf('\n')) + res_short = res_short.replace('код ответа','').replace(':','').trim() + if (res_short=='000'){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты.') + return; + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + } + if (MessageError !="" && MessageError!=undefined) { + alert(MessageStatus+"\nСообщение об ошибке:"+MessageError); + return; + }//{mes=mes+"\n Сообщение об ошибке:"+MessageError} + } + }; + +function ExecuteSuccessSetRezultReturn(Rezult, textStatus, jqX) { + console.log('ExecuteSuccessSetRezultReturn'); + LogJS('info','ExecuteSuccessSetRezultReturn') + console.log('Rezult: ',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + console.log('Status: ',Rezult.Status); + LogJS('warning','Status: '+Rezult.Status) + var state = Rezult.Status; + if (state == 1 || state == 4) { + CounGetRezult = CounGetRezult + 1; + $("#MessageStatus").text("Выполняется: Запрос №:" + CounGetRezult); + //alert("Выполняется: Запрос №:" + CounGetRezult) + var Data = { + Command: "GetRezult", + IdCommand: IdCommand, + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + setTimeout(function () { ExecuteCommandSetRezultReturn(UrlServer,User,Password,Data,null,IdCommand, ExecuteSuccessSetRezultReturn, null, false) }, 1000); + } else { // Rezult.Status <> 1 - значит команда уже выполнена + // Вывод результата выполнения команды + //alert('RezultNew:\n' + JSON.stringify(Rezult)) + //alert('status:\n' + Rezult.Status) + if (state==0){ + if (Rezult.Rezult.UniversalID){ + ord.set_kkm_UniversalID(Rezult.Rezult.UniversalID) + } + //alert('Slip:\n' + Rezult.Rezult.Slip) + if (Rezult.Rezult.Slip!=undefined){ + console.log('Slip: ',Rezult.Rezult.Slip); + LogJS('warning','Slip: '+Rezult.Rezult.Slip) + //var test = " ИП Свистунова А.Н. \r\n 353560, Краснодарский край, \r\n Славянский р-н, г \r\n Славянск-на-Кубани, \r\n Красная/Дзержинского, -, - \r\nЧЕК: 0012 ОПЛАТА ПОКУПКИ\r\n15.11.23 16:54:31\r\nТЕРМИНАЛ: 92556988\r\nКАРТА: MIR W\r\n ** 0494\r\nAID: A0000006581010 TSI: 4800\r\nTVR: 8080008000 CDA: ----\r\nСУММА (RUB) 41.00\r\n ОДОБРЕНО \r\nКОД ОТВЕТА: 000\r\nКОД АВТОРИЗАЦИИ: 4952NE\r\nССЫЛКА: 331959317247 KVR: ----\r\n================================\r\n\r" + var res_short = Rezult.Rezult.Slip.toLowerCase(); + console.log('res_short: ',res_short); + LogJS('warning','res_short: '+res_short) + if (res_short.indexOf('код ответа')>-1){ + res_short = res_short.slice(res_short.indexOf('код ответа')) + res_short = res_short.slice(0,res_short.indexOf('\n')) + res_short = res_short.replace('код ответа','').replace(':','').trim() + if (res_short=='000'){ + self.validateOrder(false) + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + if (res_short.indexOf('одобрено')>-1){ + self.validateOrder(false) + } + else{ + alert('Оплата не завершена. Попробуйте еще раз или измените тип оплаты') + return; + } + } + } + else{ + alert('Не вернулся слип чек') + } + } + } + } + + +function ExecuteCommandSetRezultReturn(UrlServer,User,Password, + Data, + currentOrder, + IdCommand, + FunSuccess, + FunError, + timeout) { + console.log('ExecuteCommandSetRezultReturn'); + LogJS('info','ExecuteCommandSetRezultReturn') + if (FunSuccess === undefined) { + FunSuccess = ExecuteSuccess; + } + if (timeout === undefined) { + timeout = timeout_term * 60000; + } + try { + if (KkmServer != undefined) { + if (typeof (Data) == "string") Data = JSON.parse(Data); + KkmServer.Execute(FunSuccess, Data); + return; + }; + } catch { }; + var JSon = JSON.stringify(Data); + $.support.cors = true; + var jqXHRvar = $.ajax({ + type: 'POST', + async: true, + timeout: timeout, + url: UrlServer + ((UrlServer == "") ? window.location.protocol + "//" + window.location.host + "/" : "/") + 'Execute', + crossDomain: true, + dataType: 'json', + contentType: 'application/json; charset=UTF-8', + processData: false, + data: JSon, + headers: (User != "" || Password != "") ? { "Authorization": "Basic " + btoa(User + ":" + Password) } : "", + success: FunSuccess, + error: FunError + }); + console.log('Ответ ККМ: ',jqXHRvar); + LogJS('warning','Ответ ККМ: '+jqXHRvar) +} \ No newline at end of file diff --git a/kkm_server_plus/static/src/js/TerminalReport.js b/kkm_server_plus/static/src/js/TerminalReport.js new file mode 100644 index 0000000..6be78c1 --- /dev/null +++ b/kkm_server_plus/static/src/js/TerminalReport.js @@ -0,0 +1,48 @@ +var glSelf; + +odoo.define('kkm_server.ClosePosPopupTerminalReport', function(require) { + "use strict"; + + const { _t } = require('web.core'); + const ClosePosPopup = require('point_of_sale.ClosePosPopup'); + const Registries = require('point_of_sale.Registries'); + const NumberBuffer = require('point_of_sale.NumberBuffer'); + + const PosKKMClosePosPopupTerminalReport = (ClosePosPopup) => class extends ClosePosPopup { + + async TerminalReport_click() { + var self = this; + glSelf = this; + console.log('TerminalReport_click'); + LogJS('info','TerminalReport_click') + var UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + var User = this.env.pos.config.User; + var Password = this.env.pos.config.Password; + var NumDevice = 0; + if (this.env.pos.config.NumDevice_term!=undefined && this.env.pos.config.NumDevice_term!=false){ + NumDevice=this.env.pos.config.NumDevice_term + } + var InnKkm = ""; + if (this.env.pos.config.InnKkm_term!=undefined && this.env.pos.config.InnKkm_term!=false){ + InnKkm=this.env.pos.config.InnKkm_term + } + var Detailed = this.env.pos.config.Detailed + var Data = { + Command: "TerminalReport", + InnKkm: InnKkm, + NumDevice: NumDevice, + Detailed: Detailed, + IdCommand: guid() + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); + } + + }; + + Registries.Component.extend(ClosePosPopup, PosKKMClosePosPopupTerminalReport); + + return PosKKMClosePosPopupTerminalReport; +}); \ No newline at end of file diff --git a/kkm_server_plus/static/src/js/UniversalIDUpdate.js b/kkm_server_plus/static/src/js/UniversalIDUpdate.js new file mode 100644 index 0000000..88c914b --- /dev/null +++ b/kkm_server_plus/static/src/js/UniversalIDUpdate.js @@ -0,0 +1,85 @@ +/** @odoo-module **/ + +import models from 'point_of_sale.models'; +import {Order} from 'point_of_sale.models'; +import Registries from "point_of_sale.Registries"; +const KKMUniversalID = (Order) => class KKMUniversalID extends Order { + constructor() { + super(...arguments); + this.UniversalID = this.UniversalID || null; + this.UniversalIDRefund = this.UniversalIDRefund || null; + this.pay_kkm_guid = this.pay_kkm_guid || null; + this.pay_cancel_kkm_guid = this.pay_cancel_kkm_guid || null; + this.pay_return_kkm_guid = this.pay_return_kkm_guid || null; + } + + set_kkm_UniversalID(UniversalID){ + this.UniversalID = UniversalID + } + + set_kkm_UniversalIDRefund(UniversalIDRefund){ + this.UniversalIDRefund = UniversalIDRefund + } + + set_kkm_pay_kkm_guid(pay_kkm_guid){ + this.pay_kkm_guid = pay_kkm_guid + } + + set_kkm_pay_cancel_kkm_guid(pay_cancel_kkm_guid){ + this.pay_cancel_kkm_guid = pay_cancel_kkm_guid + } + + set_kkm_pay_return_kkm_guid(pay_return_kkm_guid){ + this.pay_return_kkm_guid = pay_return_kkm_guid + } + + export_as_JSON() { + const json = super.export_as_JSON(...arguments) + json.UniversalID = this.UniversalID; + json.UniversalIDRefund = this.UniversalIDRefund; + json.pay_kkm_guid = this.pay_kkm_guid; + json.pay_cancel_kkm_guid = this.pay_cancel_kkm_guid; + json.pay_return_kkm_guid = this.pay_return_kkm_guid; + return json; + } + + init_from_JSON(json) { + super.init_from_JSON(...arguments); + this.UniversalID = json.UniversalID; + this.UniversalIDRefund = json.UniversalIDRefund; + this.pay_kkm_guid = json.pay_kkm_guid; + this.pay_cancel_kkm_guid = json.pay_cancel_kkm_guid; + this.pay_return_kkm_guid = json.pay_return_kkm_guid; + } + + is_electronic_pay_kkm(){ + var paymentlines = this.get_paymentlines(); + var Amount = 0; + for (var i = 0; i < paymentlines.length; i++) { + if (paymentlines[i].payment_method.type=="bank") {Amount = Amount + Math.abs(paymentlines[i].amount);} + }; + if (Amount<0) { + return false + } + else{ + return true + } + } + + is_electronic_pay_kkm_exist(){ + var paymentlines = this.get_paymentlines(); + var Amount = 0; + for (var i = 0; i < paymentlines.length; i++) { + if (paymentlines[i].payment_method.type=="bank") {Amount = Amount + Math.abs(paymentlines[i].amount);} + }; + if (Amount!=0) { + return true + } + else{ + return false + } + } + + }; + + Registries.Model.extend(Order, KKMUniversalID); \ No newline at end of file diff --git a/kkm_server_plus/static/src/js/close.js b/kkm_server_plus/static/src/js/close.js new file mode 100644 index 0000000..d6f4583 --- /dev/null +++ b/kkm_server_plus/static/src/js/close.js @@ -0,0 +1,479 @@ + +var glSelf; +var CashierName; +var CashierVATIN; + +odoo.define('kkm_server.close_kkm', function(require) { +'use strict'; + +const { Gui } = require('point_of_sale.Gui'); +const PosComponent = require('point_of_sale.PosComponent'); +const { identifyError } = require('point_of_sale.utils'); +const ProductScreen = require('point_of_sale.ProductScreen'); +const { useListener } = require("@web/core/utils/hooks"); +const Registries = require('point_of_sale.Registries'); +const PaymentScreen = require('point_of_sale.PaymentScreen'); +const { get_lot_lines } = require('point_of_sale.models'); +const { remove_orderline } = require('point_of_sale.models'); + +// вызывает поп-ап при неверном коде ЧЗ +window.myErrorPopup = function(serverResponse) { + // Use the Gui object here + Gui.showPopup('ErrorPopup', { + 'title': 'Ошибка ЧЗ', + 'body': `${serverResponse} \n Данный товар будет удален из корзины.`, + }); + }; +// удаляет продукт при неверном коде ЧЗ +window.removeCurrentOrderline = function() { + var order = glSelf.env.pos.get_order(); + var orderlines = order.get_orderlines(); + if (orderlines.length) { + var orderline = orderlines[orderlines.length - 1]; + order.remove_orderline(orderline); + } +}; + +class KKM_Close extends PosComponent { + setup() { + super.setup(); + useListener('click', this.onClick); + } + + async onClick() { + var self = this; + glSelf = this; + console.log('close_kkm'); + LogJS('info','close_kkm') + var UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + var User = this.env.pos.config.User; + var Password = this.env.pos.config.Password; + var NumDevice = 0; + if (this.env.pos.config.NumDevice_fiscal!=undefined && this.env.pos.config.NumDevice_fiscal!=false){ + NumDevice=this.env.pos.config.NumDevice_fiscal + } + var InnKkm = '' + if (this.env.pos.config.InnKkm_fiscal!=undefined && this.env.pos.config.InnKkm_fiscal!=false){ + InnKkm=this.env.pos.config.InnKkm_fiscal + } + var order = self.env.pos.get_order(); + var NotPrint = this.env.pos.config.NotPrint; + var IsBarCode = this.env.pos.config.IsBarCode; + var XRep = this.env.pos.config.XRep; + var ZRep = this.env.pos.config.ZRep; + var res = this.env.services.rpc({ + route: '/cashier/info', + params: { + cashier_id: this.env.pos.get_cashier_user_id(), + }, + }, { shadow: true }).then((value) => { CashierName = value['name']; + CashierVATIN = value['inn']; + if (XRep == true) XReport(NumDevice,UrlServer,User,Password,InnKkm); + if (ZRep == false) CloseShift(NumDevice,CashierName,CashierVATIN,NotPrint,UrlServer,User,Password,InnKkm); + if (ZRep == true) ZReport(NumDevice,UrlServer,User,Password,InnKkm); + }).catch((err) => { + CashierName = this.env.pos.config.CashierName[1]; + CashierVATIN = this.env.pos.config.CashierVATIN; + if (XRep == true) XReport(NumDevice,UrlServer,User,Password,InnKkm); + if (ZRep == false) CloseShift(NumDevice,CashierName,CashierVATIN,NotPrint,UrlServer,User,Password,InnKkm); + if (ZRep == true) ZReport(NumDevice,UrlServer,User,Password,InnKkm); + }); + } + } + +KKM_Close.template = 'KKM_Close'; + ProductScreen.addControlButton({ + component: KKM_Close, + condition: function () { + var has_enabled = false; + if (this.env.pos.config.enable_kkm_server) { + has_enabled = true; + } + return has_enabled; + }, + position: ['after', 'SetSaleOrderButton'], + }); + + Registries.Component.add(KKM_Close); + + return KKM_Close; + +}); + +function ExecuteCommand(UrlServer,User,Password, + Data, + currentOrder, + FunSuccess, + FunError, + timeout) { + console.log('ExecuteCommand'); + LogJS('info','ExecuteCommand') + if (FunSuccess === undefined) { + FunSuccess = ExecuteSuccess; + } + if (timeout === undefined) { + timeout = 60000; + } + try { + if (KkmServer != undefined) { + if (typeof (Data) == "string") Data = JSON.parse(Data); + KkmServer.Execute(FunSuccess, Data); + return; + }; + } catch { }; + var JSon = JSON.stringify(Data); + $.support.cors = true; + var jqXHRvar = $.ajax({ + type: 'POST', + async: true, + timeout: timeout, + url: UrlServer + ((UrlServer == "") ? window.location.protocol + "//" + window.location.host + "/" : "/") + 'Execute', + crossDomain: true, + dataType: 'json', + contentType: 'application/json; charset=UTF-8', + processData: false, + data: JSon, + headers: (User != "" || Password != "") ? { "Authorization": "Basic " + btoa(User + ":" + Password) } : "", + success: function (Rezult, textStatus, jqXHR) { + if (Rezult.Status == 0) { + MessageStatus = "Успешно"; + } else if (Rezult.Status == 1) { + MessageStatus = "Выполняется"; + } else if (Rezult.Status == 2) { + MessageStatus = "Ошибка"; + } else if (Rezult.Status == 3) { + MessageStatus = "Данные не найдены"; + }; + MessageError = Rezult.Error; + + var MessageCheckNumber = Rezult.CheckNumber; + var MessageSessionNumber = Rezult.SessionNumber; + var MessageLineLength = Rezult.LineLength; + var MessageAmount = Rezult.Amount; + if (currentOrder!=undefined && Rezult.UniversalID!=undefined){ + currentOrder.set_kkm_UniversalID(Rezult.UniversalID); + } + // mes = MessageStatus; + if (MessageError !="" && MessageError!=undefined) { + alert(MessageStatus+"\nСообщение об ошибке:"+MessageError); + return; + }//{mes=mes+"\n Сообщение об ошибке:"+MessageError} + //if (MessageStatus =="Успешно" && MessageError=="") {mes=""} + },//FunSuccess, + error: FunError + }); + console.log('Ответ ККМ:',jqXHRvar); + LogJS('warning','Ответ ККМ:' +jqXHRvar) +} + +//Обработка возвращаемых данных +function ExecuteSuccess(Rezult, textStatus, jqXHR) { + console.log('ExecuteSuccess'); + LogJS('info','ExecuteSuccess') + console.log('Rezult',JSON.stringify(Rezult)); + LogJS('warning','Rezult: '+JSON.stringify(Rezult)) + var ValidationResult; + if (Rezult.Status == 0) { + MessageStatus = "Успешно"; + if (Rezult.MarkingCodeValidation!=undefined){ + var i; + for (i = 0; i < Rezult.MarkingCodeValidation.length; i++) { + if (Rezult.MarkingCodeValidation[i].ValidationPR.ValidationResult||Rezult.MarkingCodeValidation[i].ValidationPR.ValidationDisabled){ + console.log('Код маркировки прошел проверку') + } else { + var serverResponse = Rezult.MarkingCodeValidation[i].ValidationPR.DecryptionResult; + console.log('Код маркировки НЕ прошел проверку'); + // попап с ошибкой + window.myErrorPopup(serverResponse); + // удалить продукт из заказа + window.removeCurrentOrderline(); + } + } + } + } else if (Rezult.Status == 1) { + MessageStatus = "Выполняется"; + } else if (Rezult.Status == 2) { + MessageStatus = "Ошибка"; + } else if (Rezult.Status == 3) { + MessageStatus = "Данные не найдены"; + }; + + MessageError = Rezult.Error; + + var MessageCheckNumber = Rezult.CheckNumber; + var MessageSessionNumber = Rezult.SessionNumber; + var MessageLineLength = Rezult.LineLength; + var MessageAmount = Rezult.Amount; + + if (MessageError !="" && MessageError!=undefined) {alert(MessageStatus+"\nСообщение об ошибке:"+MessageError)}//{mes=mes+"\n Сообщение об ошибке:"+MessageError} + //if (MessageStatus =="Успешно" && MessageError=="") {mes=""} +} + +//Генерация гуида +function guid() { + function S4() { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); + } + return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4()); +} + +//Вычисление количества копий чека +function calculateNumberCopies(DoubleReceipt) { + if (DoubleReceipt == true) { + numberCopies = 1; + } else { + numberCopies = 0; + } + return numberCopies; +} + +//Открытие смены +function OpenShift(NumDevice,CashierName,CashierVATIN,NotPrint,UrlServer,User,Password,InnKkm) { + console.log('OpenShift'); + LogJS('info','OpenShift') + var Data = { + Command: "OpenShift", + NumDevice: NumDevice, + InnKkm: InnKkm, + CashierName: CashierName, + CashierVATIN: CashierVATIN, + NotPrint: NotPrint, + IdCommand: guid(), + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); + //if (mes!="")alert("Открытие смены завершилось с сообщением: " + mes); + //mes=""; +} + +//Закрытие смены +function CloseShift(NumDevice,CashierName,CashierVATIN,NotPrint,UrlServer,User,Password,InnKkm) { + console.log('CloseShift'); + LogJS('info','CloseShift') + var Data = { + Command: "CloseShift", + NumDevice: NumDevice, + InnKkm: InnKkm, + CashierName: CashierName, + CashierVATIN: CashierVATIN, + NotPrint: NotPrint, + IdCommand: guid(), + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); + //if (mes!="")alert("Закрытие смены завершилось с сообщением: " + mes); + //mes=""; +} + +// Печать X отчета +function XReport(NumDevice,UrlServer,User,Password,InnKkm) { + console.log('XReport'); + LogJS('info','XReport') + var Data = { + Command: "XReport", + NumDevice: NumDevice, + InnKkm: InnKkm, + IdCommand: guid() + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); + //if (mes!="")alert("Печать Х-Отчета завершилась с сообщением: " + mes); + //mes=""; +} + +// Печать Z отчета +function ZReport(NumDevice,UrlServer,User,Password,InnKkm) { + console.log('ZReport'); + LogJS('info','ZReport') + var Data = { + Command: "ZReport", + NumDevice: NumDevice, + InnKkm: InnKkm, + IdCommand: guid() + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); + //if (mes!="")alert("Печать Z-Отчета завершилась с сообщением: " + mes); + //mes=""; +} + +// Печать чека +function RegisterCheck(NumDevice,TypeCheck,IsBarCode,UrlServer,User,Password,order,CashierName,CashierVATIN,InnKkm,DoubleReceipt) { + console.log('RegisterCheck'); + LogJS('info','RegisterCheck') + var client = order.get_partner(); + var receipt = order.export_for_printing(); + var orderlines = order.get_orderlines(); + var paymentlines = order.get_paymentlines(); + var Cash = 0; + var ElectronicPayment = 0; + for (var i = 0; i < paymentlines.length; i++) { + if (paymentlines[i].payment_method.type=="cash") {Cash = Cash + Math.abs(paymentlines[i].amount);} + if (paymentlines[i].payment_method.type=="bank") {ElectronicPayment = ElectronicPayment + Math.abs(paymentlines[i].amount);} + }; + var products = _.map(order.get_orderlines(), function (line) {return line.product; }); + var quantities = _.map(order.get_orderlines(), function (line) {return line.quantity; }); + var lot = _.map(order.get_orderlines(), function (line) {return line.get_lot_lines(); }); + var prices = _.map(order.get_orderlines(), function (line) {return line.price; }); + var discounts = _.map(order.get_orderlines(), function (line) {return line.discount; }); + var tax_ids = _.map(order.get_orderlines(), function (line) {return line.get_taxes(); }); + var subtotal = _.map(order.get_orderlines(), function (line) { + return line.price_subtotal_incl; + }); + var print = []; + client_email=""; + client_name=""; + client_barcode=""; + if (client!=undefined && client!=null){ + if (client.email!=undefined && client.email!=null){client_email=client.email;} + if (client.name!=undefined && client.name!=null){client_name=client.name;} + if (client.barcode!=undefined && client.barcode!=null){client_barcode=client.barcode;} + } + for (var i = 0; i < products.length; i++) { + if (lot[i] !== false) { + GoodCodeDataContent = { + BarCode: lot[i][0].lot_name, + ContainsSerialNumber: true, + AcceptOnBad: false, + } + } else { + GoodCodeDataContent = { + BarCode: products.barcode, + ContainsSerialNumber: false, + AcceptOnBad: false, + }} + console.log('GoodCodeDataContent',GoodCodeDataContent); + if (quantities[i]!=0){ + var Amount = (quantities[i]*prices[i]).toFixed(2); //(quantities[i]*prices[i]*(100-discounts[i])/100).toFixed(2); + if (discounts[i]!=undefined && discounts[i]!=null && discounts[i]!=0){ + Amount = (quantities[i]*prices[i]*(100-discounts[i])/100).toFixed(2); + // alert(quantities[i]) + // alert(prices[i]) + // alert(discounts[i]) + // alert(Amount) + } + var Department = 0; + var Tax = -1; + var tax_id = 0 + if (tax_ids.length>0 && tax_ids[i].length>0){tax_id=tax_ids[i][0].amount} + if (tax_id == 10){Tax=10} + if (tax_id == 20){Tax=20} + var SignMethodCalculation = 4; + var SignCalculationObject = 1; + var T = { PrintText: { Text: "<<->>" }, }; + var P = { + Register: { + Name: products[i].display_name, + Quantity: Math.abs(quantities[i]), + Price: Math.abs(prices[i]), + Amount: Math.abs(Amount), + Department: Department, + Tax: Tax, + EAN13: products[i].barcode, + SignMethodCalculation: SignMethodCalculation, + SignCalculationObject: SignCalculationObject, + //MeasurementUnit: "шт",//products[i].display_name , + GoodCodeData: GoodCodeDataContent, + }, + BarCode: { + BarcodeType: "EAN13" , + Barcode: products[i].barcode, + }, + }; + print.push(T); + print.push(P); + } + }; + var RRNCode = "" + var AuthorizationCode = "" + if (order.pay_cancel_kkm_guid!=undefined && order.pay_cancel_kkm_guid!=false && order.pay_cancel_kkm_guid!=null){ + //alert('TUT') + var arrayUniversalIDRefund = order.UniversalIDRefund.split(';') + arrayUniversalIDRefund.forEach(function(item, i, arr) { + var UniversalIDRefundItem = item.split(':') + if (UniversalIDRefundItem[0]==='RRN') RRNCode = UniversalIDRefundItem[1] + if (UniversalIDRefundItem[0]==='AC') AuthorizationCode = UniversalIDRefundItem[1] + //alert(UniversalIDRefundItem[0] + '---' + UniversalIDRefundItem[1]) + //alert( i + ": " + item + " (массив:" + arr + ")" ); + }); + } + //alert('RRNCode: ' + RRNCode) + //alert('AuthorizationCode: ' + AuthorizationCode) + var Data = { + Command: "RegisterCheck", + NumDevice: NumDevice, + InnKkm: InnKkm, + KktNumber: "", + Timeout: 30, + IdCommand: guid(), + IsFiscalCheck: true, + TypeCheck: TypeCheck, + NotPrint: false, + NumberCopies: calculateNumberCopies(DoubleReceipt), // подставляет кол-во копий чека (0 или 1) + CashierName: CashierName, + CashierVATIN: CashierVATIN, + ClientAddress: client_email, + ClientInfo: client_name, + ClientINN: client_barcode, + SenderEmail: "", + PlaceMarket: "", + TaxVariant: "", + PayByProcessing: false, + NumDeviceByProcessing : null, + ReceiptNumber: order.name, + PrintSlipAfterCheck: false, + PrintSlipForCashier: true, + //Если это чек возврата то возможны два поля для отмены транзакции (если не указано то по эквайрингу будет не отмена а возврат оплаты) + RRNCode: RRNCode, // RRNCode из операции эквайринга. Только для отмены оплаты! Для Оплаты или возврата оплаты не заполнять! + AuthorizationCode: AuthorizationCode, // AuthorizationCode из операции эквайринга. Только для отмены оплаты! Для Оплаты или возврата оплаты не заполнять! + CheckStrings: print, + Cash: Cash.toFixed(2), + ElectronicPayment: ElectronicPayment.toFixed(2), + //AdvancePayment: 0, + //Credit: 0, + //CashProvision: 0, + }; + + if (IsBarCode == false) { + for (var i=0; i < Data.CheckStrings.length; i++) { + if (Data.CheckStrings[i] != undefined && Data.CheckStrings[i].BarCode != undefined) { + Data.CheckStrings[i].BarCode = null; + }; + if (Data.CheckStrings[i] != undefined && Data.CheckStrings[i].PrintImage != undefined) { + Data.CheckStrings[i].PrintImage = null; + }; + }; + }; + + + Data.AgentSign = null; + Data.AgentData = null; + Data.PurveyorData = null; + for (var i = 0; i < Data.CheckStrings.length; i++) { + if (Data.CheckStrings[i] != undefined && Data.CheckStrings[i].Register != undefined) { + Data.CheckStrings[i].Register.AgentSign = null; + Data.CheckStrings[i].Register.AgentData = null; + Data.CheckStrings[i].Register.PurveyorData = null; + }; + }; + console.log('Data',JSON.stringify(Data)); + LogJS('warning','Data: '+JSON.stringify(Data)) + ExecuteCommand(UrlServer,User,Password,Data); +} + +function LogJS(Level, Message) { + glSelf.env.services.rpc({ + route: '/loggerjs/message', + params: { + Level: Level, + Message: Message, + }, + }, { shadow: true }).then((value) => { console.log(value); + }).catch((err) => {console.log(err); + }); +} diff --git a/kkm_server_plus/static/src/js/open.js b/kkm_server_plus/static/src/js/open.js new file mode 100644 index 0000000..53aa23f --- /dev/null +++ b/kkm_server_plus/static/src/js/open.js @@ -0,0 +1,95 @@ +var mes=""; +var glSelf; +var CashierName; +var CashierVATIN; + +odoo.define('kkm_server.open_kkm', function(require) { +'use strict'; + const { Gui } = require('point_of_sale.Gui'); + const PosComponent = require('point_of_sale.PosComponent'); + const { identifyError } = require('point_of_sale.utils'); + const ProductScreen = require('point_of_sale.ProductScreen'); + const { useListener } = require("@web/core/utils/hooks"); + const Registries = require('point_of_sale.Registries'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + +class KKM_Open extends PosComponent { + setup() { + super.setup(); + useListener('click', this.onClick); + } + + async onClick() { + glSelf = this; + var self = this; + console.log('open_kkm'); + LogJSOpen('info','open_kkm'); + var UrlServer = this.env.pos.config.UrlServer; + if (UrlServer=="http://localhost:5893"){UrlServer = window.location.protocol + "//" + window.location.hostname + ":5893";} + var User = this.env.pos.config.User; + var Password = this.env.pos.config.Password; + var NumDevice = 0; + if (this.env.pos.config.NumDevice_fiscal!=undefined && this.env.pos.config.NumDevice_fiscal!=false){ + NumDevice=this.env.pos.config.NumDevice_fiscal + } + var InnKkm = ''; + if (this.env.pos.config.InnKkm_fiscal!=undefined && this.env.pos.config.InnKkm_fiscal!=false){ + InnKkm=this.env.pos.config.InnKkm_fiscal + } + var order = this.env.pos.get_order(); + var NotPrint = this.env.pos.config.NotPrint; + console.log('order',order); + LogJSOpen('info','order: '+order) + var res = this.env.services.rpc({ + route: '/cashier/info', + params: { + cashier_id: this.env.pos.get_cashier_user_id(), + }, + }, { shadow: true }).then((value) => { CashierName = value['name']; + CashierVATIN = value['inn']; + OpenShift(NumDevice,CashierName,CashierVATIN,NotPrint,UrlServer,User,Password,InnKkm); + }).catch((err) => { + CashierName = this.env.pos.config.CashierName[1]; + CashierVATIN = this.env.pos.config.CashierVATIN; + OpenShift(NumDevice,CashierName,CashierVATIN,NotPrint,UrlServer,User,Password,InnKkm); + }); + + + } + } + +KKM_Open.template = 'KKM_Open'; + ProductScreen.addControlButton({ + component: KKM_Open, + condition: function () { + var has_enabled = false; + if (this.env.pos.config.enable_kkm_server) { + has_enabled = true; + } + return has_enabled; + }, + position: ['before', 'OrderlineCustomerNoteButton'], + }); + + Registries.Component.add(KKM_Open); + + return { + KKM_Open, + Gui: Gui, + }; +}); + +function LogJSOpen(Level, Message) { + glSelf.env.services.rpc({ + route: '/loggerjs/message', + params: { + Level: Level, + Message: Message, + }, + }, { shadow: true }).then((value) => { console.log(value); + }).catch((err) => {console.log(err); + }); +} + + + diff --git a/kkm_server_plus/static/src/xml/ClosePop.xml b/kkm_server_plus/static/src/xml/ClosePop.xml new file mode 100644 index 0000000..88a084d --- /dev/null +++ b/kkm_server_plus/static/src/xml/ClosePop.xml @@ -0,0 +1,19 @@ + + + + diff --git a/kkm_server_plus/static/src/xml/PayByPaymentCard.xml b/kkm_server_plus/static/src/xml/PayByPaymentCard.xml new file mode 100644 index 0000000..a41a891 --- /dev/null +++ b/kkm_server_plus/static/src/xml/PayByPaymentCard.xml @@ -0,0 +1,32 @@ + + + + diff --git a/kkm_server_plus/static/src/xml/PosIndex.xml b/kkm_server_plus/static/src/xml/PosIndex.xml new file mode 100644 index 0000000..6afd3bf --- /dev/null +++ b/kkm_server_plus/static/src/xml/PosIndex.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/kkm_server_plus/static/src/xml/UniversalIDUpdate.xml b/kkm_server_plus/static/src/xml/UniversalIDUpdate.xml new file mode 100644 index 0000000..398d880 --- /dev/null +++ b/kkm_server_plus/static/src/xml/UniversalIDUpdate.xml @@ -0,0 +1,10 @@ + + + +
+ + + Click me +
+
+
\ No newline at end of file diff --git a/kkm_server_plus/static/src/xml/service_charge.xml b/kkm_server_plus/static/src/xml/service_charge.xml new file mode 100644 index 0000000..442ce68 --- /dev/null +++ b/kkm_server_plus/static/src/xml/service_charge.xml @@ -0,0 +1,30 @@ + + + + + +
+ Закрыть смену +
+
+ +
+ Открыть смену +
+
+ +
+ X Отчет +
+
+ +
diff --git a/kkm_server_plus/views/pos.xml b/kkm_server_plus/views/pos.xml new file mode 100644 index 0000000..b2528ba --- /dev/null +++ b/kkm_server_plus/views/pos.xml @@ -0,0 +1,124 @@ + + + + pos.order.pos.order.type + pos.order + + + + + + + + + + + + + + + + pos.config.form.service.charge_new + pos.config + + + +
+
+ +
+
+
+
+
+
+
+ + + + res.users.pos.form + res.users + + + + + + + + + +
diff --git a/pos_lot_selection/README.rst b/pos_lot_selection/README.rst new file mode 100755 index 0000000..ac9cccb --- /dev/null +++ b/pos_lot_selection/README.rst @@ -0,0 +1,85 @@ +================= +POS Lot Selection +================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c4b775ef14871189a94c3978fdbc65f94ca1dafd8389725fdd049cbdf9f85071 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github + :target: https://github.com/OCA/pos/tree/16.0/pos_lot_selection + :alt: OCA/pos +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/pos-16-0/pos-16-0-pos_lot_selection + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/pos&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to pick between existing lots in POS frontend. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +1. Go to *Inventory > Settings* and set the option *Track lots or serial + numbers* +2. Chose a product that is stockable, go to its *Inventory* + tab, and set *Tracking* to *By Lots*. +3. Go to its *Sales* tab and set it as *Available in the Point of Sale*. +4. Click on *Update Qty On Hand*, chose the same location configured in the + POS you want the lot available in; write a quantity; unfold the *Lot/Serial + Number* field and pick create one if none is available yet. +5. Create a new lot with the serial number of your choice. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa +* Camptocamp + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/pos `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/pos_lot_selection/__init__.py b/pos_lot_selection/__init__.py new file mode 100755 index 0000000..0650744 --- /dev/null +++ b/pos_lot_selection/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/pos_lot_selection/__manifest__.py b/pos_lot_selection/__manifest__.py new file mode 100755 index 0000000..446d97f --- /dev/null +++ b/pos_lot_selection/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2018 Tecnativa S.L. - David Vidal +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "POS Lot Selection", + "version": "16.0.1.0.1", + "category": "Point of Sale", + "author": "Tecnativa, Camptocamp, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/pos", + "license": "AGPL-3", + "depends": [ + "point_of_sale", + ], + "assets": { + "point_of_sale.assets": [ + "pos_lot_selection/static/src/js/**/*.js", + "pos_lot_selection/static/src/xml/**/*.xml", + ], + }, + "application": False, + "installable": True, +} diff --git a/pos_lot_selection/i18n/es.po b/pos_lot_selection/i18n/es.po new file mode 100755 index 0000000..e11b30a --- /dev/null +++ b/pos_lot_selection/i18n/es.po @@ -0,0 +1,29 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_lot_selection +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-08-22 18:15+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: pos_lot_selection +#: model:ir.model,name:pos_lot_selection.model_stock_lot +msgid "Lot/Serial" +msgstr "Lote/Número de serie" + +#. module: pos_lot_selection +#. odoo-javascript +#: code:addons/pos_lot_selection/static/src/js/EditListPopup.js:0 +#, python-format +msgid "Lot/Serial Number(s) Required" +msgstr "Lote/Número(s) de serie Obligatorio" diff --git a/pos_lot_selection/i18n/it.po b/pos_lot_selection/i18n/it.po new file mode 100755 index 0000000..dea2688 --- /dev/null +++ b/pos_lot_selection/i18n/it.po @@ -0,0 +1,29 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_lot_selection +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-01-24 17:34+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: pos_lot_selection +#: model:ir.model,name:pos_lot_selection.model_stock_lot +msgid "Lot/Serial" +msgstr "Lotto/seriale" + +#. module: pos_lot_selection +#. odoo-javascript +#: code:addons/pos_lot_selection/static/src/js/EditListPopup.js:0 +#, python-format +msgid "Lot/Serial Number(s) Required" +msgstr "Richiesto numero(i) di lotto/serie" diff --git a/pos_lot_selection/i18n/pos_lot_selection.pot b/pos_lot_selection/i18n/pos_lot_selection.pot new file mode 100755 index 0000000..c54690d --- /dev/null +++ b/pos_lot_selection/i18n/pos_lot_selection.pot @@ -0,0 +1,26 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_lot_selection +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: pos_lot_selection +#: model:ir.model,name:pos_lot_selection.model_stock_lot +msgid "Lot/Serial" +msgstr "" + +#. module: pos_lot_selection +#. odoo-javascript +#: code:addons/pos_lot_selection/static/src/js/EditListPopup.js:0 +#, python-format +msgid "Lot/Serial Number(s) Required" +msgstr "" diff --git a/pos_lot_selection/models/__init__.py b/pos_lot_selection/models/__init__.py new file mode 100755 index 0000000..ee1c64f --- /dev/null +++ b/pos_lot_selection/models/__init__.py @@ -0,0 +1 @@ +from . import stock_lot diff --git a/pos_lot_selection/models/stock_lot.py b/pos_lot_selection/models/stock_lot.py new file mode 100755 index 0000000..974577f --- /dev/null +++ b/pos_lot_selection/models/stock_lot.py @@ -0,0 +1,30 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo import api, models +from odoo.tools import float_compare + + +class ProductionLot(models.Model): + _inherit = "stock.lot" + + @api.model + def get_available_lots_for_pos(self, product_id, company_id): + lots = self.sudo().search( + [ + "&", + ["product_id", "=", product_id], + "|", + ["company_id", "=", company_id], + ["company_id", "=", False], + ] + ) + + lots = lots.filtered( + lambda l: float_compare( + l.product_qty, 0, precision_digits=l.product_uom_id.rounding + ) + > 0 + ) + + return lots.mapped("name") diff --git a/pos_lot_selection/readme/DESCRIPTION.rst b/pos_lot_selection/readme/DESCRIPTION.rst new file mode 100755 index 0000000..d0ba275 --- /dev/null +++ b/pos_lot_selection/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module allows to pick between existing lots in POS frontend. diff --git a/pos_lot_selection/readme/USAGE.rst b/pos_lot_selection/readme/USAGE.rst new file mode 100755 index 0000000..cb7c750 --- /dev/null +++ b/pos_lot_selection/readme/USAGE.rst @@ -0,0 +1,9 @@ +1. Go to *Inventory > Settings* and set the option *Track lots or serial + numbers* +2. Chose a product that is stockable, go to its *Inventory* + tab, and set *Tracking* to *By Lots*. +3. Go to its *Sales* tab and set it as *Available in the Point of Sale*. +4. Click on *Update Qty On Hand*, chose the same location configured in the + POS you want the lot available in; write a quantity; unfold the *Lot/Serial + Number* field and pick create one if none is available yet. +5. Create a new lot with the serial number of your choice. diff --git a/pos_lot_selection/static/description/icon.png b/pos_lot_selection/static/description/icon.png new file mode 100755 index 0000000000000000000000000000000000000000..ca2e8601cc3a3295f625601d77fb7ae70481b957 GIT binary patch literal 4028 zcmb_fc|6ox8y~4lbuBlyJx28wN_5LoNY|=_$lA97)VyxGa1{wRF zN*D|iMq@CED61m_x-$|_tPKW-}(KXbI$j9&Uwyv&NCqh13jU2+t$Hg zFd_YuI_F_9zHtbGtAM54$$AH9_}#VijRXY+>3xRpz^AB(?nRFaF18+Cm)+2?3-%Zf z544+gVCz~KOyr`zj;4`!7rO`R*p1<<8}&1tY9FejINi*X(ipt4>gBDQ@2ob!MSoV_ zs8jnS1T|1&WwZf4%BQ*Y;dDV#?Al$Ig~e8HwbH4F)nFn@^C;)26en|d5vQogi*3#e zTA0YHq~|iKEO3?_q&kDib*>f{bwL#?{h*<-VJ{5f35yhmp#?N(eBgDtT{L}sR1nE`i0m)x=~)dU3Ev>{C$V7Cs^E3GT{3-#E5g7k&4?QLUyWLKkg50CTq|`0~fL6H02_KeGE#m%CMaM zd^GqTc>|`rZ zy*NGjNm2)IY~Xvd7nIM4O(#R--zI zqNRpWEh6{ZktWfDZiR>Ut@9tKi!yT?PwNb8oO0i?`QC>PI|VZD^TW_;ZqtYaj*Skw zRVRN5fx2INCB&PkfRugKKH_ZVK}W;GCj|GMPOrLND@BGkCRQRm4Ya7Kw)VIwCuEMN zM);%Oq?}O!_rxQ`QS$9pb$zvp%^B@f)l}(A-3Nyv@0zMgs}d~`)ZjLL_wA44&wXjm z$vPZJ+|68rKDH_JY}}!aUvwD-MzKFZ^QDpOI z(rGnXkGj4v8$0Dnc1p9a9zGP5JGJ9k5Hx5(TS!=KZ0>@vGD)I)66s|PB7(=k)yAGb zdC8ETuU;ALh)9!U?uunPSpf59En~|a})RBJbY2XF zM94%pPKb}-c!;5<+s%18-I9zPlq+^v*e-l* z-TMZc+LWV2iGzs=fuvbFr)xUy3U2p@!s6cUk`GsdrJ3Mx)%(hBqW4yDsS{ozwxO7p zJkUGW&0|brPN1ZS-S^fWV7mCZF{9XQKj#t_cC(!6c}o<2$Nfum?U%GLPLN??)pYQI zmijV+*r$AFR55kkO|-ZT_MyA-32Hb7?35n$oTm+FXuvFvj>bH|M4Er?K}BP}S~bDY zzaRh4J>i%|YVZih*x*ABpaT*40jAdD8Avm1-}^m6j}#jQAi1Isi}wU8xnTQh?BMVw z+X@LA{KqO=Q)+ghmN#mlRX4iF=Vh17lavbgVlPa zHLgGUZsf#8S*6^olk%Mbsj`Z->$Rbf<-_U6{jCq3>yE%5cOVa+Ht}_;SD_53``M#% zpJrO9xLjW=_az~1v(t2=_3h*H-EA%QC|476v~eB-qxH!*g^;ec3BEuW8lUOC>EU2U z56i*Yns}lTw+yx`M-6M8U8C;ijBPunjGc*Z+w7;SOQMpCR1>;gr>DtkwgS1 zfpi|7R@z!1v&KC0Nf{@ZKJsuvP{n03P#~u~&C~a`4A}LpajGV{S2MHnv=y4OGE04C zf>}T{tIIgeNKkVMoSC-|@SrYq~Go48d>|0Lfcte6+G# zkb?XLQ}z=e+$lX8liRevoWKvX^$mbXVJi_)&H`Hfc97u~e%j96I*he-zq5!2eZ8z< z1+6dakrAapVx!6$sniUIx+*1A9hAdRZ-Q9eQmFnTeG|J&)6K^EJ62zX*bF}f5!FHXBo48u? zoO!!G5zbjSk0vHkm+*P}=8bLJ97x-;SfW`;oY(cU^nlSJy}Q^DG3}sgUZZt-6m;BA z4SXXv(G7Mts)TO@1&9fipNcTPV0jLI^0$|8@~^+<3vkN>4$WhAQBuQu=NL(( zSsb+o_j~!m9q|Vf`sgbRJ4kPV`^pJH1TiB>f%qh_BuVCKV^5R-N-7gn3~H>KZzFVr^0~QAb(-ahAh+{{g=!xpMB|VsgAv&nKO_+KkHn z*W*Zkb_k>5?dW607Z=J54T%=p`p4GPU9V{M8E>f=8Mg#iX;!>Se}V|%XzKNxWvyn% zK0HI)`8a)VMz?1}AtXw>9|x(*c7&;`Zb36+%92!aIv zeC-5H0VwMXq%7fs`i(%1P?q_iyz(z5Ul2;SD0C(yp;Mp%<^2Y9IR19v&aLBLxRQ3AhbOa-8NJ^V4`OB>ySs2zE% z(2n`s^|f$B0T{h7NDjtz#Hn8kwpVFq_>GL;@4Eaj%A6|ofOWD+Ve__aav!w>xV|R5 z&->~0sUwdwXX<**m5=$mjA(TI+^65*Yt=h^)GTW~X5(}{xP3Z#?;3}ikB`sYw(E#O z?%8m?&SS%ev6)_LUTy*J>47MNGxCS)ZDh*{hDRztrPAqM^7!JCx0`)!xuM{qq*j&} zae6-7NRgDd-yGj5%j7#<@9WL!El7ULIC*tsc#h{DclH4D%rj!+?5l}49AtG>-+Z50 zUYHA`FUTQamfhq^g+vU__sQ+WYCW^Ba}2Xfd}%3rCLVQ!ygD*B^!PB%QEw`_Ia^+B zn$^!AyAw#6a*P>;ae%b=;9oY-6J+P_6{$~jn|EB)Y0IKE?a|77^(rLcl_1K2TYRMf zdI$B9FfokCzXCy^FbX21vTm$^Br}O638t?^J*cYwXS%PHl1$)h$;V0~#J`LsKTG_O zkFS0f!-Ycr8unMJf1+Co{ad;v%5UhFD#bszmwiA+i1%CU{xj6&kds*d$-i85enSbB zn17{&>eZE$phSYo$-fvu-CDnh9~i-P~(FnwJEog6Kzz<&c2a=q68 literal 0 HcmV?d00001 diff --git a/pos_lot_selection/static/description/index.html b/pos_lot_selection/static/description/index.html new file mode 100755 index 0000000..98522ef --- /dev/null +++ b/pos_lot_selection/static/description/index.html @@ -0,0 +1,429 @@ + + + + + +POS Lot Selection + + + +
+

POS Lot Selection

+ + +

Beta License: AGPL-3 OCA/pos Translate me on Weblate Try me on Runboat

+

This module allows to pick between existing lots in POS frontend.

+

Table of contents

+ +
+

Usage

+
    +
  1. Go to Inventory > Settings and set the option Track lots or serial +numbers
  2. +
  3. Chose a product that is stockable, go to its Inventory +tab, and set Tracking to By Lots.
  4. +
  5. Go to its Sales tab and set it as Available in the Point of Sale.
  6. +
  7. Click on Update Qty On Hand, chose the same location configured in the +POS you want the lot available in; write a quantity; unfold the Lot/Serial +Number field and pick create one if none is available yet.
  8. +
  9. Create a new lot with the serial number of your choice.
  10. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
  • Camptocamp
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/pos project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/pos_lot_selection/static/src/js/EditListPopup.js b/pos_lot_selection/static/src/js/EditListPopup.js new file mode 100755 index 0000000..a2f8e46 --- /dev/null +++ b/pos_lot_selection/static/src/js/EditListPopup.js @@ -0,0 +1,23 @@ +/* + Copyright 2022 Camptocamp SA + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +*/ +odoo.define("pos_lot_selection.EditListPopup", function (require) { + "use strict"; + + const EditListPopup = require("point_of_sale.EditListPopup"); + const Registries = require("point_of_sale.Registries"); + + const LotSelectEditListPopup = (OriginalEditListPopup) => + class extends OriginalEditListPopup { + setup() { + super.setup(); + if (this.props.title === this.env._t("Lot/Serial Number(s) Required")) { + this.props.lots = this.env.session.lots; + } + } + }; + + Registries.Component.extend(EditListPopup, LotSelectEditListPopup); + return EditListPopup; +}); diff --git a/pos_lot_selection/static/src/js/OrderWidget.js b/pos_lot_selection/static/src/js/OrderWidget.js new file mode 100755 index 0000000..5e8aefa --- /dev/null +++ b/pos_lot_selection/static/src/js/OrderWidget.js @@ -0,0 +1,29 @@ +/* + Copyright 2022 Camptocamp SA + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +*/ +odoo.define("pos_lot_selection.CustomOrderWidget", function (require) { + "use strict"; + + const Registries = require("point_of_sale.Registries"); + const OrderWidget = require("point_of_sale.OrderWidget"); + + const CustomOrderWidget = (OriginalOrderWidget) => + class extends OriginalOrderWidget { + /** + * @override + */ + async _editPackLotLines(event) { + const orderline = event.detail.orderline; + this.env.session.lots = await this.env.pos.getProductLots( + orderline.product + ); + const res = await super._editPackLotLines(...arguments); + this.env.session.lots = undefined; + return res; + } + }; + + Registries.Component.extend(OrderWidget, CustomOrderWidget); + return OrderWidget; +}); diff --git a/pos_lot_selection/static/src/js/ProductScreen.js b/pos_lot_selection/static/src/js/ProductScreen.js new file mode 100755 index 0000000..7ac0ca0 --- /dev/null +++ b/pos_lot_selection/static/src/js/ProductScreen.js @@ -0,0 +1,51 @@ +/* + Copyright 2022 Camptocamp SA (https://www.camptocamp.com). + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +*/ +odoo.define("pos_lot_selection.ProductScreen", function (require) { + "use strict"; + + const ProductScreen = require("point_of_sale.ProductScreen"); + const Registries = require("point_of_sale.Registries"); + const { Gui } = require('point_of_sale.Gui'); + + window.lotErrorPopup = function(product) { + // Use the Gui object here + Gui.showPopup('ErrorPopup', { + 'title': 'Ошибка ЧЗ', + 'body': `Введенный код маркировки отсутсвует на складе`, + }); + }; + + const PosLotSaleProductScreen = (OriginalProductScreen) => + class extends OriginalProductScreen { + /** + * @override + */ + async _getAddProductOptions(product) { + if (["serial", "lot"].includes(product.tracking)) { + this.env.session.lots = await this.env.pos.getProductLots(product); + window.availableLots = this.env.session.lots; + console.log('this.env.session.lots',this.env.session.lots); + if (this.env.session.lots.length > 0) { + const res = await super._getAddProductOptions(...arguments); + window.selectedLot = res.draftPackLotLines.newPackLotLines[0].lot_name; + if (window.availableLots.includes(window.selectedLot)) { + console.log('window.selectedLot',window.selectedLot); + return res; + } + } + window.lotErrorPopup(product); + window.lot_error = true; + return; + } + const res = await super._getAddProductOptions(...arguments); + this.env.session.lots = undefined; + + return res; + } + }; + + Registries.Component.extend(ProductScreen, PosLotSaleProductScreen); + return ProductScreen; +}); diff --git a/pos_lot_selection/static/src/js/models.js b/pos_lot_selection/static/src/js/models.js new file mode 100755 index 0000000..b128082 --- /dev/null +++ b/pos_lot_selection/static/src/js/models.js @@ -0,0 +1,35 @@ +/* + Copyright 2022 Camptocamp SA + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +*/ +odoo.define("pos_lot_selection.models", function (require) { + "use strict"; + + const {PosGlobalState} = require("point_of_sale.models"); + const Registries = require("point_of_sale.Registries"); + + const LotSelectPosGlobalState = (OriginalPosGlobalState) => + class extends OriginalPosGlobalState { + async getProductLots(product) { + try { + return await this.env.services.rpc( + { + model: "stock.lot", + method: "get_available_lots_for_pos", + kwargs: { + product_id: product.id, + company_id: this.env.session.company_id, + }, + }, + {shadow: true} + ); + } catch (error) { + console.error(error); + return []; + } + } + }; + + Registries.Model.extend(PosGlobalState, LotSelectPosGlobalState); + return PosGlobalState; +}); \ No newline at end of file diff --git a/pos_lot_selection/static/src/xml/LotSelectorPopup.xml b/pos_lot_selection/static/src/xml/LotSelectorPopup.xml new file mode 100755 index 0000000..4636bb5 --- /dev/null +++ b/pos_lot_selection/static/src/xml/LotSelectorPopup.xml @@ -0,0 +1,32 @@ + + + + + + + props.lots + + + + + + prepared_lots + + + + + + + + + + +