19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:21 +01:00
parent 7dc55599c6
commit 7f43bbbfcc
650 changed files with 45260 additions and 33436 deletions

View file

@ -1,8 +1,7 @@
# Gulf Cooperation Council - Point of Sale
GCC POS Localization
=======================================================
Adds Arabic as a secondary language on your receipt
## Installation
@ -13,36 +12,15 @@ pip install odoo-bringout-oca-ocb-l10n_gcc_pos
## Dependencies
This addon depends on:
- point_of_sale
- l10n_gcc_invoice
## Manifest Information
- **Name**: Gulf Cooperation Council - Point of Sale
- **Version**: N/A
- **Category**: Accounting/Localizations/Point of Sale
- **License**: LGPL-3
- **Installable**: False
## Source
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `l10n_gcc_pos`.
- Repository: https://github.com/OCA/OCB
- Branch: 19.0
- Path: addons/l10n_gcc_pos
## License
This package maintains the original LGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md
This package preserves the original LGPL-3 license.

View file

@ -0,0 +1 @@
from . import models

View file

@ -2,19 +2,19 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Gulf Cooperation Council - Point of Sale',
'author': 'Odoo S.A',
'category': 'Accounting/Localizations/Point of Sale',
'description': """
GCC POS Localization
=======================================================
Adds Arabic as a secondary language on your receipt
""",
'author': 'Odoo S.A.',
'license': 'LGPL-3',
'depends': ['point_of_sale', 'l10n_gcc_invoice'],
'data': [
'views/res_config_settings_views.xml',
],
'assets': {
'point_of_sale.assets': [
'l10n_gcc_pos/static/src/js/OrderReceipt.js',
'l10n_gcc_pos/static/src/css/OrderReceipt.css',
'l10n_gcc_pos/static/src/xml/OrderReceipt.xml',
'point_of_sale._assets_pos': [
'l10n_gcc_pos/static/src/**/*',
]
},
'auto_install': True,

View file

@ -0,0 +1,2 @@
from . import pos_config
from . import res_config_settings

View file

@ -0,0 +1,7 @@
from odoo import fields, models
class PosConfig(models.Model):
_inherit = 'pos.config'
l10n_gcc_dual_language_receipt = fields.Boolean(string="GCC Formatted Receipts")

View file

@ -0,0 +1,7 @@
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
l10n_gcc_dual_language_receipt = fields.Boolean(related='pos_config_id.l10n_gcc_dual_language_receipt', readonly=False)

View file

@ -0,0 +1,25 @@
import { PosOrder } from "@point_of_sale/app/models/pos_order";
import { patch } from "@web/core/utils/patch";
patch(PosOrder.prototype, {
get isGccCountry() {
return ["SA", "AE", "BH", "OM", "QA", "KW"].includes(this.company.country_id?.code);
},
/**
* If the order is empty (there are no products)
* and all "pay_later" payments are negative,
* we are settling a customer's account.
* If the module pos_settle_due is not installed,
* the function always returns false (since "pay_later" doesn't exist)
* @returns {boolean} true if the current order is a settlement, else false
*/
isSettlement() {
return (
this.isEmpty() &&
!!this.payment_ids.filter(
(paymentline) =>
paymentline.payment_method_id.type === "pay_later" && paymentline.amount < 0
).length
);
},
});

View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-inherit="point_of_sale.ReceiptHeader" t-inherit-mode="extension">
<xpath expr="//div[hasclass('pos-receipt-contact')]" position="after">
<t t-set="show_title" t-value="false"/>
<t t-set="show_simplified_title" t-value="false"/>
<t t-if="order.isGccCountry and show_title">
<br/>
<br/>
<div class="pos-receipt-header">
<t t-if="!show_simplified_title">
<t t-if="order.config_id.l10n_gcc_dual_language_receipt">
<span id="title_en" t-translation="off">Tax Invoice</span> /
<span id="title_ar" t-translation="off">الفاتورة الضريبية</span>
</t>
<t t-else="">
<t t-if="order.user_id.partner_id.lang != 'ar_001'">
<span name="title" t-translation="off">Tax Invoice</span>
</t>
<t t-else="">
<span name="title" t-translation="off">الفاتورة الضريبية</span>
</t>
</t>
</t>
<t t-else="">
<t t-if="order.config_id.l10n_gcc_dual_language_receipt">
<span name="title_simplified_en" t-translation="off">Simplified Tax Invoice</span> /
<span name="title_simplified_ar" t-translation="off">فاتورة ضريبية مبسطة</span>
</t>
<t t-else="">
<t t-if="order.user_id.partner_id.lang != 'ar_001'">
<span name="title_simplified" t-translation="off">Simplified Tax Invoice</span>
</t>
<t t-else="">
<span name="title_simplified" t-translation="off">فاتورة ضريبية مبسطة</span>
</t>
</t>
</t>
</div>
</t>
</xpath>
<xpath expr="//div[hasclass('cashier')]" position="attributes">
<attribute name="t-if">!order.isGccCountry or !order.config_id.l10n_gcc_dual_language_receipt</attribute>
</xpath>
<xpath expr="//div[hasclass('cashier')]" position="after">
<div t-elif="order.isGccCountry and order.config_id.l10n_gcc_dual_language_receipt" t-translation="off">
<div>Served by / خدم بواسطة <t t-esc="order.getCashierName()"/></div>
</div>
</xpath>
</t>
<t t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension">
<xpath expr="//t[@t-as='subtotal']//span" position="attributes">
<attribute name="t-if">!order.isGccCountry or !order.config_id.l10n_gcc_dual_language_receipt</attribute>
</xpath>
<xpath expr="//t[@t-as='subtotal']//span" position="after">
<span name="subtotal_span_dual" t-elif="order.isGccCountry and order.config_id.l10n_gcc_dual_language_receipt" class="text-nowrap mw-100" t-translation="off">Subtotal / الإجمالي الفرعي</span>
</xpath>
<xpath expr="//span[hasclass('label-rounding')]" position="attributes">
<attribute name="t-if">!order.isGccCountry or !order.config_id.l10n_gcc_dual_language_receipt</attribute>
</xpath>
<xpath expr="//span[hasclass('label-rounding')]" position="after">
<span name="rounding_span_dual" t-elif="order.isGccCountry and order.config_id.l10n_gcc_dual_language_receipt" class="label-rounding" t-translation="off">Rounding / التقريب</span>
</xpath>
<xpath expr="//span[hasclass('label-total')]" position="attributes">
<attribute name="t-if">!order.isGccCountry or !order.config_id.l10n_gcc_dual_language_receipt</attribute>
</xpath>
<xpath expr="//span[hasclass('label-total')]" position="after">
<span name="total_span_dual" t-elif="order.isGccCountry and order.config_id.l10n_gcc_dual_language_receipt" class="label-total" t-translation="off">Total / اﻹجمالي</span>
</xpath>
<xpath expr="//span[hasclass('label-discount')]" position="attributes">
<attribute name="t-if">!order.isGccCountry or !order.config_id.l10n_gcc_dual_language_receipt</attribute>
</xpath>
<xpath expr="//span[hasclass('label-discount')]" position="after">
<span name="discounts_span_dual" t-elif="order.isGccCountry and order.config_id.l10n_gcc_dual_language_receipt" class="label-discount" t-translation="off">Discounts / الخصومات</span>
</xpath>
<xpath expr="//span[hasclass('label-change')]" position="attributes">
<attribute name="t-if">!order.isGccCountry or !order.config_id.l10n_gcc_dual_language_receipt</attribute>
</xpath>
<xpath expr="//span[hasclass('label-change')]" position="after">
<span name="change_span_dual" t-elif="order.isGccCountry and order.config_id.l10n_gcc_dual_language_receipt" class="label-change" t-translation="off">Change / الباقي</span>
</xpath>
</t>
</templates>

View file

@ -1,20 +0,0 @@
odoo.define('l10n_gcc_pos.OrderReceipt', function (require) {
'use strict';
const OrderReceipt = require('point_of_sale.OrderReceipt')
const Registries = require('point_of_sale.Registries');
const OrderReceiptGCC = OrderReceipt =>
class extends OrderReceipt {
get receiptEnv() {
let receipt_render_env = super.receiptEnv;
let receipt = receipt_render_env.receipt;
const country = receipt_render_env.order.pos.company.country;
receipt.is_gcc_country = country ? ['SA', 'AE', 'BH', 'OM', 'QA', 'KW'].includes(country && country.code) : false;
return receipt_render_env;
}
}
Registries.Component.extend(OrderReceipt, OrderReceiptGCC)
return OrderReceiptGCC
});

View file

@ -1,140 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension" owl="1">
<xpath expr="//div[hasclass('pos-receipt-contact')]" position="after">
<t t-if="receipt.is_gcc_country">
<br/>
<br/>
<div class="pos-receipt-header">
<span id="title_english" t-translation="off">Tax Invoice</span>
</div>
<div class="pos-receipt-header">
<span id="title_arabic" t-translation="off">الفاتورة الضريبية</span>
</div>
</t>
</xpath>
<xpath expr="//t[@t-esc='receipt.cashier']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//t[@t-esc='receipt.cashier']/.." position="after">
<div t-if="receipt.is_gcc_country" t-translation="off">
<div>Served by / خدم بواسطة <t t-esc="receipt.cashier"/></div>
</div>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.subtotal)']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.subtotal)']/.." position="after">
<div t-if="receipt.is_gcc_country" t-translation="off">
Subtotal / الإجمالي الفرعي
<span t-esc="env.pos.format_currency(receipt.subtotal)" class="pos-receipt-right-align"/>
</div>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.total_with_tax)']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.total_with_tax)']/.." position="after">
<div t-if="receipt.is_gcc_country" class="pos-receipt-amount pos-receipt-amount-arabic" t-translation="off">
TOTAL / الإجمالي
<span t-esc="env.pos.format_currency(receipt.total_with_tax)" class="pos-receipt-right-align"/>
</div>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.rounding_applied)']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.rounding_applied)']/.." position="after">
<div t-if="receipt.is_gcc_country" class="pos-receipt-amount pos-receipt-amount-arabic" t-translation="off">
Rounding / التقريب
<span t-esc="env.pos.format_currency(receipt.rounding_applied)" class="pos-receipt-right-align"/>
</div>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.total_rounded)']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.total_rounded)']/.." position="after">
<div t-if="receipt.is_gcc_country" class="pos-receipt-amount pos-receipt-amount-arabic" t-translation="off">
To Pay / للسداد
<span t-esc="env.pos.format_currency(receipt.total_rounded)" class="pos-receipt-right-align"/>
</div>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.change)']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.change)']/.." position="after">
<div t-if="receipt.is_gcc_country" class="pos-receipt-amount receipt-change pos-receipt-amount-arabic" t-translation="off">
CHANGE / الباقي
<span t-esc="env.pos.format_currency(receipt.change)" class="pos-receipt-right-align"/>
</div>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.total_discount)']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.total_discount)']/.." position="after">
<div t-if="receipt.is_gcc_country" t-translation="off">
Discounts / الخصومات
<span t-esc="env.pos.format_currency(receipt.total_discount)" class="pos-receipt-right-align"/>
</div>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.total_tax)']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//span[@t-esc='env.pos.format_currency(receipt.total_tax)']/.." position="after">
<div t-if="receipt.is_gcc_country" t-translation="off">
Total Taxes / إجمالي الضرائب
<span t-esc="env.pos.format_currency(receipt.total_tax)" class="pos-receipt-right-align"/>
</div>
</xpath>
</t>
<t t-inherit="point_of_sale.OrderLinesReceipt" t-inherit-mode="extension" owl="1">
<xpath expr="//t[@t-if='isSimple(line)']/div/span[hasclass('price_display')]" position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//t[@t-if='isSimple(line)']/WrappedProductNameLines" position="after">
<div class="responsive-price" t-if="receipt.is_gcc_country">
<div class="pos-receipt-left-padding" style="display: inline-flex;">
<div t-translation="off">Taxes / الضرائب</div>:<span t-esc="env.pos.format_currency_no_symbol(line.tax)" style="margin-left: 5px"/>
</div>
<span t-esc="env.pos.format_currency_no_symbol(line.price_display)" class="price_display pos-receipt-right-align"/>
</div>
</xpath>
<xpath expr="//t[@t-esc='line.unit_name']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//t[@t-esc='line.unit_name']/.." position="after">
<t t-if="receipt.is_gcc_country">
<div class="pos-receipt-left-padding">
<t t-esc="Math.round(line.quantity * Math.pow(10, env.pos.dp['Product Unit of Measure'])) / Math.pow(10, env.pos.dp['Product Unit of Measure'])"/>
<t t-if="!line.is_in_unit" t-esc="line.unit_name" />
x
<t t-esc="env.pos.format_currency_no_symbol(line.price_display_one)" />
</div>
<div class="responsive-price">
<div class="pos-receipt-left-padding" style="display: inline-flex;">
<div t-translation="off">Taxes / الضرائب</div>:<span t-esc="env.pos.format_currency_no_symbol(line.tax)" style="margin-left: 5px"/>
</div>
<span t-esc="env.pos.format_currency_no_symbol(line.price_display)" class="price_display pos-receipt-right-align"/>
</div>
</t>
</xpath>
<xpath expr="//t[@t-esc='line.discount']/.." position="attributes">
<attribute name="t-if">!receipt.is_gcc_country</attribute>
</xpath>
<xpath expr="//t[@t-esc='line.discount']/.." position="after">
<div class="pos-receipt-left-padding" style="display: inline-flex;" t-if="receipt.is_gcc_country" t-translation="off">
<div>Discount / الخصم</div>: <t t-esc="line.discount" />%
</div>
</xpath>
</t>
</templates>

View file

@ -0,0 +1,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_gcc_pos

View file

@ -0,0 +1,28 @@
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.addons.point_of_sale.tests.test_generic_localization import TestGenericLocalization
from odoo.tests import tagged
@tagged('post_install', '-at_install', 'post_install_l10n')
class TestGenericGCC(TestGenericLocalization):
@classmethod
@AccountTestInvoicingCommon.setup_country('sa')
def setUpClass(cls):
super().setUpClass()
if cls.env['ir.module.module']._get('l10n_sa_edi').state == 'installed':
cls.skipTest(cls, "l10n_sa_edi should not be installed")
cls.main_pos_config.company_id.name = 'Generic GCC'
cls.company.write({
'email': 'info@company.saexample.com',
'phone': '+966 51 234 5678',
'street2': 'Testomania',
'vat': '311111111111113',
'state_id': cls.env['res.country.state'].create({
'name': 'Riyadh',
'code': 'RYA',
'country_id': cls.company.country_id.id
}),
'street': 'Al Amir Mohammed Bin Abdul Aziz Street',
'city': 'المدينة المنورة',
'zip': '42317',
})

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_config_settings_view_form_inherit_l10n_gcc_pos" model="ir.ui.view">
<field name="name">res.config.settins.view.form.inherit</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="point_of_sale.res_config_settings_view_form"/>
<field name="arch" type="xml">
<block id="pos_bills_and_receipts_section" position="inside">
<setting string="Gulf Cooperation Council Format"
invisible="not company_id.l10n_gcc_country_is_gcc"
help="Add Arabic as a secondary language to your receipt">
<field name="l10n_gcc_dual_language_receipt"/>
</setting>
</block>
</field>
</record>
</data>
</odoo>

View file

@ -1,13 +1,15 @@
[project]
name = "odoo-bringout-oca-ocb-l10n_gcc_pos"
version = "16.0.0"
description = "Gulf Cooperation Council - Point of Sale - Odoo addon"
description = "Gulf Cooperation Council - Point of Sale -
Odoo addon
"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ocb-point_of_sale>=16.0.0",
"odoo-bringout-oca-ocb-l10n_gcc_invoice>=16.0.0",
"odoo-bringout-oca-ocb-point_of_sale>=19.0.0",
"odoo-bringout-oca-ocb-l10n_gcc_invoice>=19.0.0",
"requests>=2.25.1"
]
readme = "README.md"
@ -17,7 +19,7 @@ classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]