from odoo.exceptions import ValidationError from odoo.tests import tagged from odoo.addons.account.tests.common import AccountTestInvoicingHttpCommon from unittest.mock import patch from freezegun import freeze_time @tagged('post_install_l10n', 'post_install', '-at_install') class TestPosQris(AccountTestInvoicingHttpCommon): """ Testing QRIS payment via PoS """ @classmethod @AccountTestInvoicingHttpCommon.setup_chart_template('id') def setUpClass(cls): super().setUpClass() cls.company_data['company'].qr_code = True cls.company_data['company'].partner_id.update({ 'country_id': cls.env.ref('base.id').id, 'city': 'Jakarta', }) cls.acc_qris_id = cls.env['res.partner.bank'].create({ 'acc_number': '123456789012345678', 'partner_id': cls.company_data['company'].partner_id.id, 'l10n_id_qris_api_key': 'apikey', 'l10n_id_qris_mid': 'mid', }) cls.product_1 = cls.env['product.product'].create({ 'name': 'Test Product', 'available_in_pos': True, 'list_price': 1000, 'taxes_id': False, }) # Create user. cls.pos_user = cls.env['res.users'].create({ 'name': 'A simple PoS man!', 'login': 'pos_user', 'password': 'pos_user', 'groups_id': [ (4, cls.env.ref('base.group_user').id), (4, cls.env.ref('point_of_sale.group_pos_user').id), (4, cls.env.ref('account.group_account_invoice').id), ], }) cls.company = cls.company_data['company'] cls.pos_receivable_bank = cls.copy_account(cls.company.account_default_pos_receivable_account_id, {'name': 'POS Receivable Bank'}) cls.outstanding_bank = cls.copy_account(cls.outbound_payment_method_line.payment_account_id, {'name': 'Outstanding Bank'}) cls.company_data['default_journal_bank'].write({'bank_account_id': cls.acc_qris_id.id}) cls.bank_pm = cls.env['pos.payment.method'].create({ 'name': 'Cash', 'journal_id': cls.company_data['default_journal_bank'].id, 'receivable_account_id': cls.pos_receivable_bank.id, 'outstanding_account_id': cls.outstanding_bank.id, 'company_id': cls.company.id, }) cls.qris_pm = cls.env['pos.payment.method'].create({ 'name': 'QRIS', 'journal_id': cls.company_data['default_journal_bank'].id, 'receivable_account_id': cls.pos_receivable_bank.id, 'outstanding_account_id': cls.outstanding_bank.id, 'company_id': cls.company.id, 'payment_method_type': 'qr_code', 'qr_code_method': 'id_qr' }) cls.main_pos_config = cls.env['pos.config'].create({ 'name': 'Shop', 'module_pos_restaurant': False, # Make sure there is one extra payment method for the tour tests to work. # Because if the tour only use the qr payment method, the total amount won't be displayed, # causing the tour test to fail. 'payment_method_ids': [(4, cls.bank_pm.id), (4, cls.qris_pm.id)], }) def test_qris_transaction_allow_pos_order(self): """ pos.order model should be created """ self.env['l10n_id.qris.transaction'].create({ 'model': 'pos.order', 'model_id': '1234512345' }) self.env['l10n_id.qris.transaction'].create({ 'model': 'account.move', 'model_id': '1' }) with self.assertRaises(ValidationError): self.env['l10n_id.qris.transaction'].create({ 'model': 'new.model', 'model_id': '1', }) def test_qris_link_with_pos_order(self): """ Test whether it's possible to link QRIS transaction with a pos.order record through UUID field instead of id """ self.main_pos_config.with_user(self.pos_user).open_ui() pos_order = self.env['pos.order'].create({ 'company_id': self.env.company.id, 'session_id': self.main_pos_config.current_session_id.id, 'partner_id': self.partner_a.id, 'access_token': '1234567890', 'uuid': '1234512345', 'lines': [(0, 0, { 'name': "OL/0001", 'product_id': self.product_1.id, 'price_unit': 10000, 'discount': 0.0, 'qty': 1.0, 'tax_ids': False, 'price_subtotal': 10, 'price_subtotal_incl': 10, })], 'amount_tax': 0, 'amount_total': 10, 'amount_paid': 10.0, 'amount_return': 10.0, }) qris_transaction = self.env['l10n_id.qris.transaction'].create({ 'model': 'pos.order', 'model_id': '1234512345', }) record = qris_transaction._get_record() self.assertEqual(pos_order, record) def test_tour_qris_payment_fail(self): """ Add products, show QR code and confirm. When confirming, the result will be status unpaid and so it should trigger a warning dialog informing it""" def _patched_make_qris_request(endpoint, params): if endpoint == 'show_qris.php': return { "status": "success", "data": { "qris_content": "Test Content", "qris_request_date": "2024-02-27 11:13:42", "qris_invoiceid": "413255111", "qris_nmid": "ID1020021181745" } } elif endpoint == 'checkpaid_qris.php': return { "status": "failed", "data": { "qris_status": "unpaid" } } self.main_pos_config.with_user(self.pos_user).open_ui() with patch('odoo.addons.l10n_id.models.res_bank._l10n_id_make_qris_request', side_effect=_patched_make_qris_request): self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PaymentScreenQRISPaymentFail', login="pos_user") def test_tour_qris_payment_success(self): """ Successful fetching status should proceed next to go to receipt screen""" def _patched_make_qris_request(endpoint, params): if endpoint == 'show_qris.php': return { "status": "success", "data": { "qris_content": "Test Content", "qris_request_date": "2024-02-27 11:13:42", "qris_invoiceid": "413255111", "qris_nmid": "ID1020021181745" } } elif endpoint == 'checkpaid_qris.php': return { "status": "success", "data": { "qris_status": "paid", "qris_payment_customername": "Zainal Arief", "qris_payment_methodby": "Sakuku" }, "qris_api_version_code": "2206091709" } self.main_pos_config.with_user(self.pos_user).open_ui() with patch('odoo.addons.l10n_id.models.res_bank._l10n_id_make_qris_request', side_effect=_patched_make_qris_request): self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PaymentScreenQRISPaymentSuccess', login="pos_user") @freeze_time("2024-02-27 04:15:00") def test_only_call_api_call_once(self): """ Simulate generating QR, cancel the popup, when we click show QR again, it shouldn't trigger to fetch new QR code from QRIS""" def _patched_make_qris_request(endpoint, params): if endpoint == 'show_qris.php': return { "status": "success", "data": { "qris_content": "Test Content", "qris_request_date": "2024-02-27 11:13:42", "qris_invoiceid": "413255111", "qris_nmid": "ID1020021181745" } } elif endpoint == 'checkpaid_qris.php': return { "status": "success", "data": { "qris_status": "paid", "qris_payment_customername": "Zainal Arief", "qris_payment_methodby": "Sakuku" }, "qris_api_version_code": "2206091709" } self.main_pos_config.with_user(self.pos_user).open_ui() with patch('odoo.addons.l10n_id.models.res_bank._l10n_id_make_qris_request', side_effect=_patched_make_qris_request) as patched: self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PayementScreenQRISFetchQR', login="pos_user") self.assertEqual(patched.call_count, 1) @freeze_time("2024-02-27 04:15:00") def test_qris_change_amount(self): """ Test that when user changes the amount of order after generating QRIS QR for the first time, it should request for new QR code afterwards. Therefore, there should be 2 API calls instead""" def _patched_make_qris_request(endpoint, params): if endpoint == 'show_qris.php': return { "status": "success", "data": { "qris_content": "Test Content", "qris_request_date": "2024-02-27 11:13:42", "qris_invoiceid": "413255111", "qris_nmid": "ID1020021181745" } } self.main_pos_config.with_user(self.pos_user).open_ui() self.main_pos_config.current_session_id.set_opening_control(0, 'notes') with patch('odoo.addons.l10n_id.models.res_bank._l10n_id_make_qris_request', side_effect=_patched_make_qris_request) as patched: self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PayementScreenQRISChangeAmount', login="pos_user") self.assertEqual(patched.call_count, 2)