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

@ -10,38 +10,15 @@ pip install odoo-bringout-oca-ocb-l10n_in_pos
## Dependencies
This addon depends on:
- l10n_in
- point_of_sale
## Manifest Information
- **Name**: Indian - Point of Sale
- **Version**: 1.0
- **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_in_pos`.
- Repository: https://github.com/OCA/OCB
- Branch: 19.0
- Path: addons/l10n_in_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
- Reports: doc/REPORTS.md
- Security: doc/SECURITY.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

@ -3,7 +3,6 @@
{
'name': 'Indian - Point of Sale',
'icon': '/l10n_in/static/description/icon.png',
'version': '1.0',
'description': """GST Point of Sale""",
'category': 'Accounting/Localizations/Point of Sale',
@ -11,15 +10,25 @@
'l10n_in',
'point_of_sale'
],
'data': [
'views/pos_order_line_views.xml',
'views/product_view.xml',
'views/res_config_settings_views.xml',
'data/pos_bill_data.xml',
],
'demo': [
'data/product_demo.xml',
],
'auto_install': True,
'assets': {
'point_of_sale.assets': [
'l10n_in_pos/static/src/js/**/*',
'l10n_in_pos/static/src/xml/**/*',
'point_of_sale._assets_pos': [
'l10n_in/static/src/helpers/hsn_summary.js',
'l10n_in_pos/static/src/**/*',
],
'web.assets_tests': [
'l10n_in_pos/static/tests/tours/**/*',
],
},
'author': 'Odoo S.A.',
'license': 'LGPL-3',
}

View file

@ -0,0 +1,8 @@
<odoo>
<data noupdate="1">
<record model="pos.bill" id="500_00" forcecreate="0">
<field name="name">500.00</field>
<field name="value">500.00</field>
</record>
</data>
</odoo>

View file

@ -1,57 +1,40 @@
<odoo>
<data noupdate="1">
<record id="point_of_sale.desk_organizer" model="product.product">
<record id="product.desk_organizer" model="product.product">
<field name="l10n_in_hsn_code">9403</field>
<field name="l10n_in_hsn_description">Other furniture and parts thereof.</field>
</record>
<record id="point_of_sale.desk_pad" model="product.product">
<record id="product.desk_pad" model="product.product">
<field name="l10n_in_hsn_code">9403</field>
<field name="l10n_in_hsn_description">Other furniture and parts thereof.</field>
</record>
<record id="point_of_sale.led_lamp" model="product.product">
<field name="l10n_in_hsn_code">8539.50.00</field>
<field name="l10n_in_hsn_description">Light-emitting diode (LED) lamps</field>
<field name="l10n_in_hsn_code">85395000</field>
</record>
<record id="point_of_sale.letter_tray" model="product.product">
<field name="l10n_in_hsn_code">4819.60.00</field>
<field name="l10n_in_hsn_description">Box files, letter trays, storage boxes and similar articles, of a kind used in offices, shops or the like</field>
<field name="l10n_in_hsn_code">48196000</field>
</record>
<record id="point_of_sale.magnetic_board" model="product.product">
<field name="l10n_in_hsn_code">3921.90.99</field>
<field name="l10n_in_hsn_description">Other plates, sheets film , foil and strip, of plastics</field>
<field name="l10n_in_hsn_code">39219099</field>
</record>
<record id="point_of_sale.product_product_consumable" model="product.product">
<field name="l10n_in_hsn_code">8443.32.90</field>
<field name="l10n_in_hsn_description">Other, capable of connecting to an automatic data processing machine or to a network</field>
</record>
<record id="point_of_sale.monitor_stand" model="product.product">
<record id="product.monitor_stand" model="product.product">
<field name="l10n_in_hsn_code">9403</field>
<field name="l10n_in_hsn_description">Other furniture and parts thereof.</field>
</record>
<record id="point_of_sale.newspaper_rack" model="product.product">
<field name="l10n_in_hsn_code">9403.10.90</field>
<field name="l10n_in_hsn_description">Metal furniture of a kind used in offices</field>
<field name="l10n_in_hsn_code">94031090</field>
</record>
<record id="point_of_sale.small_shelf" model="product.product">
<field name="l10n_in_hsn_code">9403.10.90</field>
<field name="l10n_in_hsn_description">Metal furniture of a kind used in offices</field>
<field name="l10n_in_hsn_code">94031090</field>
</record>
<record id="point_of_sale.product_product_tip" model="product.product">
<field name="l10n_in_hsn_code">8209.00.90</field>
<field name="l10n_in_hsn_description">Plates, sticks, tips and the like for tools, unmounted, of cermets.</field>
<field name="l10n_in_hsn_code">82090090</field>
</record>
<record id="point_of_sale.wall_shelf" model="product.product">
<field name="l10n_in_hsn_code">9403.10.90</field>
<field name="l10n_in_hsn_description">Metal furniture of a kind used in offices</field>
<field name="l10n_in_hsn_code">94031090</field>
</record>
<record id="point_of_sale.whiteboard" model="product.product">
<field name="l10n_in_hsn_code">3926.10.99</field>
<field name="l10n_in_hsn_description">Office supplies of a kind classified as stationary other than pins,clips, and writing instruments</field>
<field name="l10n_in_hsn_code">39261099</field>
</record>
<record id="point_of_sale.whiteboard_pen" model="product.product">
<field name="l10n_in_hsn_code">9608</field>
<field name="l10n_in_hsn_description">Ball point pens; felt tipped and other porous-tipped pens and markers; fountain pens, stylograph pens and other pens; duplicating stylos; propelling or sliding pencils; pen-holders, pencilholders and similar holders; parts (including caps and clips) of the foregoing articles, othe than those of heading 9609
</field>
</record>
</data>
</odoo>

View file

@ -0,0 +1,290 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_in_pos
#
# Weblate <noreply-mt-weblate@weblate.org>, 2025.
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 19:06+0000\n"
"PO-Revision-Date: 2025-11-17 03:12+0000\n"
"Last-Translator: Weblate <noreply-mt-weblate@weblate.org>\n"
"Language-Team: Hindi <https://translate.odoo.com/projects/odoo-19-l10n/"
"l10n_in_pos/hi/>\n"
"Language: hi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n==0 || n==1);\n"
"X-Generator: Weblate 5.12.2\n"
#. module: l10n_in_pos
#: model_terms:ir.ui.view,arch_db:l10n_in_pos.res_config_settings_view_form_l10n_in_pos_inherit
msgid ""
"<span>Please note that the kiosk for INR currency only works with Razorpay "
"terminal</span>"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid ""
"As POS sales are reported in GSTR-1, HSN/SAC codes must be set on all "
"taxable products."
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "CESS"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "CGST"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_pos_bill
msgid "Coins/Bills"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_res_company
msgid "Companies"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Company address is missing"
msgstr ""
#. module: l10n_in_pos
#: model_terms:ir.actions.act_window,help:l10n_in_pos.action_missing_hsn_product
msgid "Create a new product variant"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_move__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_move_line__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_tax__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_bill__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_config__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order_line__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_session__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_product__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_template__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_res_company__display_name
msgid "Display Name"
msgstr "डिस्प्ले का नाम"
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_account_tax
msgid "ETA tax codes mixin"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/product_info_popup/product_info_popup.js:0
msgid "GST:"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid "Go to Products"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Go to company configuration"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "HSN Code"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "HSN Code:"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_product__l10n_in_hsn_missing_in_pos
msgid "HSN Missing in POS"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "HSN Summary"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order_line__l10n_in_hsn_code
#: model_terms:ir.ui.view,arch_db:l10n_in_pos.product_tree_hsn_code
msgid "HSN/SAC Code"
msgstr "HSN/SAC कोड"
#. module: l10n_in_pos
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_move__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_move_line__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_tax__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_bill__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_config__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order_line__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_session__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_product__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_template__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_res_company__id
msgid "ID"
msgstr "आईडी"
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "IGST"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "INVOICE"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid ""
"If you know the HSN codes, you can go to Products and set the missing ones "
"there."
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_account_move
msgid "Journal Entry"
msgstr "जर्नल एंट्री"
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_account_move_line
msgid "Journal Item"
msgstr "जर्नल आइटम"
#. module: l10n_in_pos
#: model:ir.actions.act_window,name:l10n_in_pos.action_missing_hsn_product
msgid "Missing HSN Products"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid "Missing HSN/SAC Codes"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Ok"
msgstr "ठीक है"
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "Phone:"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_pos_config
msgid "Point of Sale Configuration"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_pos_order_line
msgid "Point of Sale Order Lines"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_pos_order
msgid "Point of Sale Orders"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_pos_session
msgid "Point of Sale Session"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_product_template
msgid "Product"
msgstr "प्रॉडक्ट"
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_product_product
msgid "Product Variant"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "Rate%"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "SGST"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Set the address of your company (Don't forget the State field)"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid ""
"Some products are missing this information, which prevents the session from "
"being closed."
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "Tax Invoice"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/product_info_popup/product_info_popup.js:0
msgid "Total GST:"
msgstr ""
#. module: l10n_in_pos
#: model_terms:ir.actions.act_window,help:l10n_in_pos.action_missing_hsn_product
msgid ""
"You must define an HSN for every product you sell through\n"
" the point of sale interface."
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Your company"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "needs to have a correct address in order validate the invoice."
msgstr ""

View file

@ -4,10 +4,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0+e\n"
"Project-Id-Version: Odoo Server 19.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-12 07:24+0000\n"
"PO-Revision-Date: 2023-06-12 07:24+0000\n"
"POT-Creation-Date: 2026-03-06 19:06+0000\n"
"PO-Revision-Date: 2026-03-06 19:06+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@ -16,22 +16,185 @@ msgstr ""
"Plural-Forms: \n"
#. module: l10n_in_pos
#: code:addons/l10n_in_pos/models/pos_config.py:0
#, python-format
msgid "Go to Company configuration"
#: model_terms:ir.ui.view,arch_db:l10n_in_pos.res_config_settings_view_form_l10n_in_pos_inherit
msgid ""
"<span>Please note that the kiosk for INR currency only works with Razorpay "
"terminal</span>"
msgstr ""
#. module: l10n_in_pos
#. openerp-web
#: code:addons/l10n_in_pos/static/src/xml/pos_receipt.xml:0
#, python-format
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid ""
"As POS sales are reported in GSTR-1, HSN/SAC codes must be set on all "
"taxable products."
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "CESS"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "CGST"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_pos_bill
msgid "Coins/Bills"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_res_company
msgid "Companies"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Company address is missing"
msgstr ""
#. module: l10n_in_pos
#: model_terms:ir.actions.act_window,help:l10n_in_pos.action_missing_hsn_product
msgid "Create a new product variant"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_move__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_move_line__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_tax__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_bill__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_config__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order_line__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_session__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_product__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_template__display_name
#: model:ir.model.fields,field_description:l10n_in_pos.field_res_company__display_name
msgid "Display Name"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_account_tax
msgid "ETA tax codes mixin"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/product_info_popup/product_info_popup.js:0
msgid "GST:"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid "Go to Products"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Go to company configuration"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "HSN Code"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "HSN Code:"
msgstr ""
#. module: l10n_in_pos
#. openerp-web
#: code:addons/l10n_in_pos/static/src/xml/pos_receipt.xml:0
#, python-format
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_product__l10n_in_hsn_missing_in_pos
msgid "HSN Missing in POS"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "HSN Summary"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order_line__l10n_in_hsn_code
#: model_terms:ir.ui.view,arch_db:l10n_in_pos.product_tree_hsn_code
msgid "HSN/SAC Code"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_move__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_move_line__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_account_tax__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_bill__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_config__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_order_line__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_pos_session__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_product__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_product_template__id
#: model:ir.model.fields,field_description:l10n_in_pos.field_res_company__id
msgid "ID"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "IGST"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "INVOICE"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid ""
"If you know the HSN codes, you can go to Products and set the missing ones "
"there."
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_account_move
msgid "Journal Entry"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_account_move_line
msgid "Journal Item"
msgstr ""
#. module: l10n_in_pos
#: model:ir.actions.act_window,name:l10n_in_pos.action_missing_hsn_product
msgid "Missing HSN Products"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid "Missing HSN/SAC Codes"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Ok"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "Phone:"
msgstr ""
@ -40,15 +203,84 @@ msgstr ""
msgid "Point of Sale Configuration"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_pos_order_line
msgid "Point of Sale Order Lines"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_pos_order
msgid "Point of Sale Orders"
msgstr ""
#. module: l10n_in_pos
#: code:addons/l10n_in_pos/models/pos_config.py:0
#, python-format
msgid ""
"Your company %s needs to have a correct address in order to open the session.\n"
"Set the address of your company (Don't forget the State field)"
#: model:ir.model,name:l10n_in_pos.model_pos_session
msgid "Point of Sale Session"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_product_template
msgid "Product"
msgstr ""
#. module: l10n_in_pos
#: model:ir.model,name:l10n_in_pos.model_product_product
msgid "Product Variant"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "Rate%"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "SGST"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Set the address of your company (Don't forget the State field)"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/hsn_code_dialog/hsn_code_dialog.xml:0
msgid ""
"Some products are missing this information, which prevents the session from "
"being closed."
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/screens/receipt_screen/pos_receipt.xml:0
msgid "Tax Invoice"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/product_info_popup/product_info_popup.js:0
msgid "Total GST:"
msgstr ""
#. module: l10n_in_pos
#: model_terms:ir.actions.act_window,help:l10n_in_pos.action_missing_hsn_product
msgid ""
"You must define an HSN for every product you sell through\n"
" the point of sale interface."
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "Your company"
msgstr ""
#. module: l10n_in_pos
#. odoo-javascript
#: code:addons/l10n_in_pos/static/src/app/components/popups/company_state_dialog/company_state_dialog.xml:0
msgid "needs to have a correct address in order validate the invoice."
msgstr ""

View file

@ -1,6 +1,14 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import pos_order
from . import pos_order_line
from . import product
from . import product_template
from . import account_move
from . import account_move_line
from . import account_tax
from . import pos_bill
from . import pos_session
from . import pos_order
from . import pos_config
from . import res_company

View file

@ -0,0 +1,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
class AccountMove(models.Model):
_inherit = 'account.move'
@api.depends('pos_session_ids', 'reversed_pos_order_id')
def _compute_l10n_in_state_id(self):
res = super()._compute_l10n_in_state_id()
to_compute = self.filtered(lambda m: m.country_code == 'IN' and not m.l10n_in_state_id and m.journal_id.type == 'general' and (m.pos_session_ids or m.reversed_pos_order_id))
for move in to_compute:
move.l10n_in_state_id = move.company_id.state_id
return res

View file

@ -0,0 +1,41 @@
from odoo import models
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
def _get_l10n_in_gstr_section(self, tax_tags_dict):
result = super()._get_l10n_in_gstr_section(tax_tags_dict)
eco_9_5_tag = tax_tags_dict['eco_9_5']
all_gst_tags = tax_tags_dict['cgst'] + tax_tags_dict['sgst'] + tax_tags_dict['igst'] + tax_tags_dict['cess']
def get_section(line):
tax_tags = line.tax_tag_ids.ids
if any(tag == eco_9_5_tag for tag in tax_tags):
return 'sale_eco_9_5'
if any(tag in tax_tags for tag in all_gst_tags):
return 'sale_b2cs'
if any(tax.l10n_in_tax_type == 'nil_rated' for tax in line.tax_ids):
return 'sale_nil_rated'
if any(tax.l10n_in_tax_type == 'exempt' for tax in line.tax_ids):
return 'sale_exempt'
if any(tax.l10n_in_tax_type == 'non_gst' for tax in line.tax_ids):
return 'sale_non_gst_supplies'
return 'sale_out_of_scope'
in_pos_closing_and_reversed_lines = self.filtered(
lambda l: l.move_id.country_code == 'IN'
and l.move_id.move_type == 'entry'
and l.display_type in ('product', 'tax')
and (l.move_id.sudo().pos_session_ids or l.move_id.sudo().reversed_pos_order_id)
)
if not in_pos_closing_and_reversed_lines:
return result
move_lines_by_gstr_section = in_pos_closing_and_reversed_lines.grouped(get_section)
for section, lines in move_lines_by_gstr_section.items():
result[section] = result.get(section, self.env['account.move.line']) | lines
return result

View file

@ -0,0 +1,11 @@
from odoo import api, models
class AccountTax(models.Model):
_inherit = 'account.tax'
@api.model
def _load_pos_data_fields(self, config):
fields = super()._load_pos_data_fields(config)
fields += ['l10n_in_gst_tax_type']
return fields

View file

@ -0,0 +1,15 @@
from odoo import api, models
from odoo.fields import Domain
class PosBill(models.Model):
_inherit = "pos.bill"
@api.model
def _load_pos_data_domain(self, data, config):
domain = super()._load_pos_data_domain(data, config)
if self.env.company.country_code == 'IN':
domain = Domain.AND([domain, [('value', '>=', 1.0), ('value', '<=', 2000.0)]])
return domain

View file

@ -1,24 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.exceptions import RedirectWarning
from odoo.tools.translate import _
class PosConfig(models.Model):
_inherit = 'pos.config'
def open_ui(self):
for config in self:
if config.company_id.country_id.code == 'IN' and not config.company_id.state_id:
msg = _("Your company %s needs to have a correct address in order to open the session.\n"
"Set the address of your company (Don't forget the State field)") % (config.company_id.name)
action = {
"view_mode": "form",
"res_model": "res.company",
"type": "ir.actions.act_window",
"res_id" : config.company_id.id,
"views": [[self.env.ref("base.view_company_form").id, "form"]],
}
raise RedirectWarning(msg, action, _('Go to Company configuration'))
return super(PosConfig, self).open_ui()
def _is_quantities_set(self):
return self.company_id.l10n_in_is_gst_registered or super()._is_quantities_set()

View file

@ -1,20 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo import models
class PosOrder(models.Model):
_inherit = 'pos.order'
def _prepare_invoice_vals(self):
vals = super()._prepare_invoice_vals()
if self.session_id.company_id.country_id.code == 'IN':
partner = self.partner_id
l10n_in_gst_treatment = partner.l10n_in_gst_treatment
if not l10n_in_gst_treatment and partner.country_id and partner.country_id.code != 'IN':
l10n_in_gst_treatment = 'overseas'
if not l10n_in_gst_treatment:
l10n_in_gst_treatment = partner.vat and 'regular' or 'consumer'
vals['l10n_in_gst_treatment'] = l10n_in_gst_treatment
return vals
def _prepare_product_aml_dict(self, base_line_vals, update_base_line_vals, rate, sign):
res = super()._prepare_product_aml_dict(base_line_vals, update_base_line_vals, rate, sign)
if self.company_id.account_fiscal_country_id.code == 'IN':
res.update({
'l10n_in_hsn_code': base_line_vals['l10n_in_hsn_code'],
})
return res

View file

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class PosOrderLine(models.Model):
_inherit = "pos.order.line"
l10n_in_hsn_code = fields.Char(string="HSN/SAC Code", compute="_compute_l10n_in_hsn_code", store=True, readonly=False, copy=False)
@api.depends('product_id')
def _compute_l10n_in_hsn_code(self):
indian_lines = self.filtered(lambda line: line.company_id.account_fiscal_country_id.code == 'IN')
(self - indian_lines).l10n_in_hsn_code = False
for line in indian_lines:
if line.product_id:
line.l10n_in_hsn_code = line.product_id.l10n_in_hsn_code
@api.model
def _load_pos_data_fields(self, config):
params = super()._load_pos_data_fields(config)
if self.env.company.country_id.code == 'IN':
params += ['l10n_in_hsn_code']
return params
def _prepare_base_line_for_taxes_computation(self):
res = super()._prepare_base_line_for_taxes_computation()
if self.company_id.l10n_in_is_gst_registered:
res.update({
'l10n_in_hsn_code': self.l10n_in_hsn_code,
})
return res

View file

@ -1,13 +1,46 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class PosSession(models.Model):
_inherit = 'pos.session'
def _loader_params_product_product(self):
result = super()._loader_params_product_product()
result['search_params']['fields'].append('l10n_in_hsn_code')
return result
def _get_sale_key(self, base_line):
res = super()._get_sale_key(base_line)
if self.config_id.company_id.l10n_in_is_gst_registered:
res.update({
'uom_id': base_line['uom_id'].id,
'l10n_in_hsn_code': base_line['l10n_in_hsn_code'],
})
return res
def _get_sale_vals(self, key, sale_vals):
res = super()._get_sale_vals(key, sale_vals)
if self.config_id.company_id.l10n_in_is_gst_registered:
res.update({
'l10n_in_hsn_code': key['l10n_in_hsn_code'],
'product_uom_id': key['uom_id'],
})
return res
def set_missing_hsn_codes_in_pos_orders(self):
self.ensure_one()
PosOrderLine = self.env['pos.order.line']
base_domain = [
('order_id.session_id', '=', self.id),
('order_id.account_move', '=', False),
('l10n_in_hsn_code', '=', False),
('tax_ids', '!=', False),
]
# Lines where product already has HSN
lines_with_product_hsn = PosOrderLine.search(
base_domain + [('product_id.l10n_in_hsn_code', '!=', False)]
)
for line in lines_with_product_hsn:
line.l10n_in_hsn_code = line.product_id.l10n_in_hsn_code
# Lines where product itself is missing HSN
missing_hsn_lines = PosOrderLine.search(
base_domain + [('product_id.l10n_in_hsn_code', '=', False)]
)
return missing_hsn_lines

View file

@ -0,0 +1,70 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class ProductProduct(models.Model):
_inherit = "product.product"
l10n_in_hsn_missing_in_pos = fields.Boolean(
search="_search_l10n_in_hsn_missing_in_pos",
store=False,
string="HSN Missing in POS",
)
def _search_l10n_in_hsn_missing_in_pos(self, operator, value):
if operator not in ("=", "!="):
return NotImplemented
domain = [
("order_id.session_id.state", "!=", "closed"),
("order_id.account_move", "=", False),
("product_id.product_tmpl_id.l10n_in_hsn_code", "=", False),
("product_id.product_tmpl_id.taxes_id", "!=", False),
]
grouped_data = self.env["pos.order.line"]._read_group(
domain,
aggregates=["__count"],
groupby=["product_id"],
)
product_ids = [row[0].id for row in grouped_data]
positive = (operator == "=" and value) or (operator == "!=" and not value)
return [("id", "in" if positive else "not in", product_ids)]
@api.model
def l10n_in_get_hsn_code_action(self):
"""
Returns the action to open the HSN code dialog with the given products.
FIXME: This method dynamically creates a view at runtime.
FIXME: Remove this method and the dynamic view in master.
"""
action_xml_id = 'l10n_in_pos.action_missing_hsn_product'
action = self.env.ref(action_xml_id, False)
if not action:
action = self.env['ir.actions.act_window'].sudo().create({
'name': 'Missing HSN Products',
'type': 'ir.actions.act_window',
'res_model': 'product.product',
'view_mode': 'kanban,list,form',
'domain': [
('l10n_in_hsn_missing_in_pos', '=', True)
],
'help': """
<p class="o_view_nocontent_smiling_face">
Create a new product variant
</p>
<p>
You must define an HSN for every product you sell through
the point of sale interface.
</p>
""",
})
self.env['ir.model.data']._update_xmlids([{
'xml_id': action_xml_id,
'record': action,
}])
return action_xml_id

View file

@ -0,0 +1,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
class ProductTemplate(models.Model):
_inherit = "product.template"
@api.model
def _load_pos_data_fields(self, config):
fields = super()._load_pos_data_fields(config)
if self.env.company.country_id.code == 'IN':
fields += ['l10n_in_hsn_code']
return fields

View file

@ -0,0 +1,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
class ResCompany(models.Model):
_inherit = "res.company"
@api.model
def _load_pos_data_fields(self, config):
fields = super()._load_pos_data_fields(config)
if self.env.company.country_id.code == 'IN':
fields += ['l10n_in_is_gst_registered']
return fields

View file

@ -0,0 +1,15 @@
import { CashMovePopup } from "@point_of_sale/app/components/popups/cash_move_popup/cash_move_popup";
import { patch } from "@web/core/utils/patch";
import { companyStateDialog } from "@l10n_in_pos/app/components/popups/company_state_dialog/company_state_dialog";
patch(CashMovePopup.prototype, {
async confirm() {
await this.pos.data.read("res.company", [this.pos.company.id]);
if (this.pos.company.country_id?.code === "IN" && !this.pos.company.state_id) {
this.dialog.add(companyStateDialog);
return;
}
return await super.confirm();
},
});

View file

@ -0,0 +1,34 @@
import { ClosePosPopup } from "@point_of_sale/app/components/popups/closing_popup/closing_popup";
import { patch } from "@web/core/utils/patch";
import { useService } from "@web/core/utils/hooks";
import { companyStateDialog } from "@l10n_in_pos/app/components/popups/company_state_dialog/company_state_dialog";
import { hsnCodeDialog } from "@l10n_in_pos/app/components/popups/hsn_code_dialog/hsn_code_dialog";
patch(ClosePosPopup.prototype, {
setup() {
super.setup();
this.orm = useService("orm");
},
async confirm() {
await this.pos.data.read("res.company", [this.pos.company.id]);
if (this.pos.company.country_id?.code === "IN" && !this.pos.company.state_id) {
this.dialog.add(companyStateDialog);
return;
}
if (this.pos.company.l10n_in_is_gst_registered) {
const missingHsnLines = await this.orm.call(
"pos.session",
"set_missing_hsn_codes_in_pos_orders",
[this.pos.session.id]
);
if (missingHsnLines?.length) {
this.dialog.add(hsnCodeDialog);
return;
}
}
return await super.confirm();
},
});

View file

@ -0,0 +1,23 @@
import { Dialog } from "@web/core/dialog/dialog";
import { Component } from "@odoo/owl";
import { usePos } from "@point_of_sale/app/hooks/pos_hook";
export class companyStateDialog extends Component {
static components = { Dialog };
static template = "l10n_in_pos.companyStateDialog";
static props = {
close: Function,
};
setup() {
this.pos = usePos();
}
redirect() {
window.location = "/odoo/companies/" + this.pos.company.id;
}
onClose() {
this.props.close();
}
}

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="l10n_in_pos.companyStateDialog">
<Dialog size="'md'" title.translate="Company address is missing">
<div>
<p>Your company <span><t t-esc="this.pos.company.name"/></span> needs to have a correct address in order validate the invoice.</p>
<p>Set the address of your company (Don't forget the State field)</p>
</div>
<t t-set-slot="footer">
<div class="modal-footer-right d-flex gap-2">
<button class="button icon btn btn-lg btn-primary" t-on-click="redirect">
Go to company configuration
</button>
<button class="button btn btn-secondary" t-on-click="onClose">
Ok
</button>
</div>
</t>
</Dialog>
</t>
</templates>

View file

@ -0,0 +1,27 @@
import { Component } from "@odoo/owl";
import { Dialog } from "@web/core/dialog/dialog";
import { usePos } from "@point_of_sale/app/hooks/pos_hook";
export class hsnCodeDialog extends Component {
static components = { Dialog };
static template = "l10n_in_pos.hsnCodeDialog";
static props = {
close: Function,
};
setup() {
this.pos = usePos();
}
async redirect() {
// Close dialog first
this.props.close();
const action_xml_id = await this.pos.data.call(
"product.product",
"l10n_in_get_hsn_code_action",
[]
);
const url = `/odoo/action-${action_xml_id}`;
window.open(url, "_blank");
}
}

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="l10n_in_pos.hsnCodeDialog">
<Dialog size="'md'" title.translate="Missing HSN/SAC Codes">
<div>
<p>
As POS sales are reported in GSTR-1, HSN/SAC codes must be set on all taxable products.<br/>
Some products are missing this information, which prevents the session from being closed.
</p>
<p>
If you know the HSN codes, you can go to Products and set the missing ones there.
</p>
</div>
<t t-set-slot="footer">
<div class="modal-footer-right d-flex gap-2">
<button class="button icon btn btn-lg btn-primary" t-on-click="redirect">
Go to Products
</button>
</div>
</t>
</Dialog>
</t>
</templates>

View file

@ -0,0 +1,13 @@
import { _t } from "@web/core/l10n/translation";
import { ProductInfoPopup } from "@point_of_sale/app/components/popups/product_info_popup/product_info_popup";
import { patch } from "@web/core/utils/patch";
patch(ProductInfoPopup.prototype, {
get vatLabel() {
return this.pos.company.country_id.code === "IN" ? _t("GST:") : super.vatLabel;
},
get totalVatLabel() {
return this.pos.company.country_id.code === "IN" ? _t("Total GST:") : super.totalVatLabel;
},
});

View file

@ -0,0 +1,14 @@
import { patch } from "@web/core/utils/patch";
import { CustomerDisplayPosAdapter } from "@point_of_sale/app/customer_display/customer_display_adapter";
patch(CustomerDisplayPosAdapter.prototype, {
formatOrderData(order) {
super.formatOrderData(order);
this.data.onlinePaymentData = { ...(order.onlinePaymentData || {}) };
},
getOrderlineData(line) {
const data = super.getOrderlineData(line);
data.l10n_in_hsn_code = line.getProduct().l10n_in_hsn_code || "";
return data;
},
});

View file

@ -0,0 +1,15 @@
import { InvoiceButton } from "@point_of_sale/app/screens/ticket_screen/invoice_button/invoice_button";
import { patch } from "@web/core/utils/patch";
import { companyStateDialog } from "@l10n_in_pos/app/components/popups/company_state_dialog/company_state_dialog";
patch(InvoiceButton.prototype, {
async click() {
await this.pos.data.read("res.company", [this.pos.company.id]);
if (this.pos.company.country_id?.code === "IN" && !this.pos.company.state_id) {
this.dialog.add(companyStateDialog);
return;
}
return await super.click();
},
});

View file

@ -0,0 +1,15 @@
import { PaymentScreen } from "@point_of_sale/app/screens/payment_screen/payment_screen";
import { patch } from "@web/core/utils/patch";
import { companyStateDialog } from "@l10n_in_pos/app/components/popups/company_state_dialog/company_state_dialog";
patch(PaymentScreen.prototype, {
async toggleIsToInvoice() {
await this.pos.data.read("res.company", [this.pos.company.id]);
if (this.pos.company.country_id?.code === "IN" && !this.pos.company.state_id) {
this.dialog.add(companyStateDialog);
return;
}
return await super.toggleIsToInvoice();
},
});

View file

@ -0,0 +1,43 @@
import { PosOrder } from "@point_of_sale/app/models/pos_order";
import { patch } from "@web/core/utils/patch";
import { accountTaxHelpers } from "@account/helpers/account_tax";
import { formatCurrency } from "@point_of_sale/app/models/utils/currency";
patch(PosOrder.prototype, {
get isInCompany() {
return this.company.country_id?.code === "IN";
},
_prepareL10nInHsnSummary() {
const company = this.company;
const orderLines = this.lines;
// If each line is negative, we assume it's a refund order.
// It's a normal order if it doesn't contain a line (useful for pos_settle_due).
// TODO: Properly differentiate refund orders from normal ones.
const documentSign = this.isRefund ? -1 : 1;
const baseLines = orderLines.map((line) =>
accountTaxHelpers.prepare_base_line_for_taxes_computation(
line,
line.prepareBaseLineForTaxesComputationExtraValues({
quantity: documentSign * line.qty,
})
)
);
accountTaxHelpers.add_tax_details_in_base_lines(baseLines, company);
accountTaxHelpers.round_base_lines_tax_details(baseLines, company);
const hsnSummary = accountTaxHelpers.l10n_in_get_hsn_summary_table(baseLines, false);
if (hsnSummary) {
for (const item of hsnSummary.items) {
for (const key of [
"tax_amount_igst",
"tax_amount_cgst",
"tax_amount_sgst",
"tax_amount_cess",
]) {
item[key] = formatCurrency(item[key], this.currency);
}
}
}
return hsnSummary;
},
});

View file

@ -0,0 +1,15 @@
import { PosOrderline } from "@point_of_sale/app/models/pos_order_line";
import { patch } from "@web/core/utils/patch";
patch(PosOrderline.prototype, {
setup(vals) {
return super.setup(...arguments);
},
// EXTENDS 'point_of_sale'
prepareBaseLineForTaxesComputationExtraValues(customValues = {}) {
const extraValues = super.prepareBaseLineForTaxesComputationExtraValues(customValues);
extraValues.l10n_in_hsn_code = this.product_id?.l10n_in_hsn_code;
return extraValues;
},
});

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<t t-name="l10n_in_pos.ReceiptHeader" t-inherit="point_of_sale.ReceiptHeader" t-inherit-mode="extension">
<xpath expr="//div[hasclass('pos-receipt-customer')]" position="inside">
<t t-if="order.partner_id and order.company?.country_id?.code == 'IN'">
<t t-if="order.partner_id.phone">
<div>
<span>Phone: </span>
<t t-out="order.partner_id.phone" />
</div>
</t>
<br />
</t>
</xpath>
<xpath expr="//span[hasclass('pos-receipt-vat')]" position="after">
<span t-if="order.isInCompany and !order.isToInvoice()" class="pos-receipt-vat">
INVOICE <t t-esc="order.invoiceName" />
</span>
</xpath>
<xpath expr="//span[@class='ticket-name-prefix']" position="attributes">
<attribute name="t-if">!order.isInCompany</attribute>
</xpath>
<xpath expr="//span[@class='ticket-name-prefix']" position="after">
<span t-if="order.isInCompany" class="ticket-name-prefix">
Tax Invoice
</span>
</xpath>
</t>
<t t-name="l10n_in_pos.OrderReceipt" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension">
<xpath expr="//Orderline" position="inside">
<t t-if="line.l10n_in_hsn_code and header.company.country_id?.code === 'IN'">
<div class="pos-receipt-left-padding">
<span>HSN Code: </span>
<t t-out="line.l10n_in_hsn_code"/>
</div>
</t>
</xpath>
<xpath expr="//div[@class='before-footer']" position="after">
<t t-set="l10n_in_hsn_summary" t-value="order._prepareL10nInHsnSummary()"/>
<table t-if="l10n_in_hsn_summary and header.company.country_id?.code === 'IN' and l10n_in_hsn_summary.items.length > 0"
class="l10n_in_hsn_summary_table pt-3 w-100">
<tr>
<th class="text-center fw-bolder" colspan="6">HSN Summary</th>
</tr>
<tr>
<th class="text-center">HSN Code</th>
<th class="text-center">Rate%</th>
<th class="text-center" t-if="l10n_in_hsn_summary.has_gst">CGST</th>
<th class="text-center" t-if="l10n_in_hsn_summary.has_gst">SGST</th>
<th class="text-center" t-if="l10n_in_hsn_summary.has_igst">IGST</th>
<th class="text-center" t-if="l10n_in_hsn_summary.has_cess">CESS</th>
</tr>
<tr t-foreach="l10n_in_hsn_summary.items" t-as="item" t-key="item_index">
<td class="text-center" t-out="item.l10n_in_hsn_code"/>
<td class="text-center"><t t-out="item.rate"/> %</td>
<td class="text-center" t-if="l10n_in_hsn_summary.has_gst" t-out="item.tax_amount_cgst"/>
<td class="text-center" t-if="l10n_in_hsn_summary.has_gst" t-out="item.tax_amount_sgst"/>
<td class="text-center" t-if="l10n_in_hsn_summary.has_igst" t-out="item.tax_amount_igst"/>
<td class="text-center" t-if="l10n_in_hsn_summary.has_cess" t-out="item.tax_amount_cess"/>
</tr>
</table>
</xpath>
</t>
</templates>

View file

@ -1,17 +0,0 @@
odoo.define('l10n_in_pos.receipt', function (require) {
"use strict";
var { Orderline } = require('point_of_sale.models');
const Registries = require('point_of_sale.Registries');
const L10nInOrderline = (Orderline) => class L10nInOrderline extends Orderline {
export_for_printing() {
var line = super.export_for_printing(...arguments);
line.l10n_in_hsn_code = this.get_product().l10n_in_hsn_code;
return line;
}
}
Registries.Model.extend(Orderline, L10nInOrderline);
});

View file

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<t t-name="OrderReceipt" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension" owl="1">
<xpath expr="//div[hasclass('orderlines')]" position="before">
<t t-if="receipt.partner and env.pos.company.country and env.pos.company.country.code == 'IN'">
<div class="pos-receipt-center-align">
<div><t t-esc="receipt.partner.name" /></div>
<t t-if="receipt.partner.phone">
<div>
<span>Phone: </span>
<t t-esc="receipt.partner.phone" />
</div>
</t>
<br />
</div>
</t>
</xpath>
</t>
<t t-name="OrderLinesReceipt" t-inherit="point_of_sale.OrderLinesReceipt" t-inherit-mode="extension" owl="1">
<xpath expr="//t[@t-foreach='receipt.orderlines']" position="inside">
<t t-if="line.l10n_in_hsn_code and env.pos.company.country and env.pos.company.country.code == 'IN'">
<div class="pos-receipt-left-padding">
<span>HSN Code: </span>
<t t-esc="line.l10n_in_hsn_code"/>
</div>
</t>
</xpath>
</t>
</templates>

View file

@ -0,0 +1,19 @@
import { registry } from "@web/core/registry";
import * as Chrome from "@point_of_sale/../tests/pos/tours/utils/chrome_util";
import * as Dialog from "@point_of_sale/../tests/generic_helpers/dialog_util";
import * as ProductScreen from "@point_of_sale/../tests/pos/tours/utils/product_screen_util";
registry.category("web_tour.tours").add("test_product_long_press_india", {
steps: () =>
[
Chrome.startPoS(),
Dialog.confirm("Open Register"),
ProductScreen.longPressProduct("Test Product"),
Dialog.is(),
{
content: "Check that VAT label is present in the product details popup",
trigger: ".section-financials .vat-label:contains('GST')",
},
Chrome.endTour(),
].flat(),
});

View file

@ -0,0 +1,58 @@
import * as Chrome from "@point_of_sale/../tests/pos/tours/utils/chrome_util";
import * as Dialog from "@point_of_sale/../tests/generic_helpers/dialog_util";
import * as ProductScreen from "@point_of_sale/../tests/pos/tours/utils/product_screen_util";
import * as PaymentScreen from "@point_of_sale/../tests/pos/tours/utils/payment_screen_util";
import { registry } from "@web/core/registry";
export function addDocument(documentParams) {
const steps = [];
for (const values of documentParams) {
steps.push(...ProductScreen.addOrderline(values.product, values.quantity));
if (values.discount) {
steps.push(ProductScreen.addDiscount(values.discount));
}
}
steps.push(ProductScreen.clickPayButton());
return steps;
}
registry.category("web_tour.tours").add("test_l10n_in_hsn_summary_pos", {
steps: () =>
[
Chrome.startPoS(),
Dialog.confirm("Open Register"),
...addDocument([
{ product: "product_1_1", quantity: "2" },
{ product: "product_1_2", quantity: "1" },
{ product: "product_1_3", quantity: "5" },
{ product: "product_1_4", quantity: "2" },
{ product: "product_1_5", quantity: "1" },
{ product: "product_1_6", quantity: "5" },
]),
PaymentScreen.totalIs("5,129.0"),
PaymentScreen.clickPaymentMethod("Bank"),
PaymentScreen.remainingIs("0.0"),
PaymentScreen.clickValidate(),
{
isActive: ["desktop"], // not rendered on mobile
trigger:
'.receipt-screen .l10n_in_hsn_summary_table tr:nth-child(3) td:nth-child(3):contains("57.50")',
},
{
isActive: ["desktop"], // not rendered on mobile
trigger:
'.receipt-screen .l10n_in_hsn_summary_table tr:nth-child(3) td:nth-child(4):contains("57.50")',
},
{
isActive: ["desktop"], // not rendered on mobile
trigger:
'.receipt-screen .l10n_in_hsn_summary_table tr:nth-child(4) td:nth-child(3):contains("207.00")',
},
{
isActive: ["desktop"], // not rendered on mobile
trigger:
'.receipt-screen .l10n_in_hsn_summary_table tr:nth-child(4) td:nth-child(4):contains("207.00")',
},
].flat(),
});

View file

@ -0,0 +1,8 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_in_pos
from . import test_hsn_summary
from . import common
from . import test_pos_flow
from . import test_gstr_section
from . import test_product_screen

View file

@ -0,0 +1,90 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from contextlib import contextmanager
from odoo.fields import Command
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
from odoo.addons.account.tests.common import TestTaxCommon
class TestInPosBase(TestPoSCommon):
"""
Base class for Indian GSTR and POS-related test cases.
This class sets up the company, products, and configuration required
for any test involving GSTR in a POS environment.
"""
@classmethod
@TestTaxCommon.setup_country('in')
def setUpClass(cls):
super().setUpClass()
country_in_id = cls.env.ref("base.in").id
cls.company_data["company"].write({
"vat": "24AAGCC7144L6ZE",
"state_id": cls.env.ref("base.state_in_gj").id,
"street": "street1",
"city": "city1",
"zip": "123456",
"country_id": country_in_id,
"l10n_in_is_gst_registered": True,
})
cls.config = cls.basic_config
cls.gst_5 = cls.env['account.chart.template'].ref('sgst_sale_5')
cls.nil_rated = cls.env['account.chart.template'].ref('nil_rated_sale')
country_in = cls.env.ref('base.in')
state_in_gj = cls.env.ref('base.state_in_gj')
cls.partner_a.write({
'name': "Partner Intra State",
'vat': '24ABCPM8965E1ZE',
'state_id': state_in_gj.id,
'country_id': country_in.id,
'street': "Karansinhji Rd",
'street2': "Karanpara",
'city': "Rajkot",
'zip': "360001",
})
# Common product setup for POS tests
cls._setup_products()
@classmethod
def _setup_products(cls):
"""Sets up products for POS testing with GST."""
cls.product_a.write({
'available_in_pos': True,
'l10n_in_hsn_code': '1111',
'list_price': 100,
'taxes_id': [Command.set(cls.gst_5.ids)], # Tax: 5
})
cls.product_b.write({
'available_in_pos': True,
'l10n_in_hsn_code': '2222',
'list_price': 200,
'taxes_id': [Command.set(cls.gst_5.ids)], # Tax: 10
})
cls.product_c = cls.env['product.product'].create({
'name': 'Product C',
'available_in_pos': True,
'list_price': 300,
'taxes_id': [Command.set(cls.nil_rated.ids)], # Tax: 0
})
@contextmanager
def with_pos_session(self):
"""Opens a new POS session and ensures it is closed properly."""
session = self.open_new_session(0.0)
yield session
session.post_closing_cash_details(0.0)
session.close_session_from_ui()
def _create_order(self, ui_data):
"""Helper to create a POS order from UI data."""
order_data = self.create_ui_order_data(**ui_data)
results = self.env['pos.order'].sync_from_ui([order_data])
return self.env['pos.order'].browse([o['id'] for o in results['pos.order']])
@classmethod
def _create_categ_anglo(cls):
# Stock Valuation support is currently not implemented for Indian localization.
# Once support is added, this method override will be removed.
return False

View file

@ -0,0 +1,37 @@
from odoo.addons.l10n_in_pos.tests.common import TestInPosBase
from odoo.tests import tagged
from datetime import date
TEST_DATE = date(2023, 5, 20)
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestPOSGstrSection(TestInPosBase):
def test_b2cs_gstr_section_with_pos_order(self):
with self.with_pos_session() as session:
self._create_order({
'pos_order_lines_ui_args': [
(self.product_a, 2.0), # 2 units of product A
(self.product_b, 2.0), # 2 units of product B
],
'payments': [(self.bank_pm1, 630.0)],
})
session.action_pos_session_closing_control()
pos_entry_lines = session.move_id.line_ids
for line in pos_entry_lines.filtered(lambda l: l.display_type in ('product, tax')):
self.assertEqual(line.l10n_in_gstr_section, 'sale_b2cs')
def test_nil_rated_gstr_section_with_pos_order(self):
with self.with_pos_session() as session:
self._create_order({
'pos_order_lines_ui_args': [
(self.product_c, 3.0), # 3 units of product C
],
'payments': [(self.bank_pm1, 900.0)],
'customer': self.partner_a,
})
session.action_pos_session_closing_control()
pos_entry_lines = session.move_id.line_ids
for line in pos_entry_lines.filtered(lambda l: l.display_type in ('product, tax')):
self.assertEqual(line.l10n_in_gstr_section, 'sale_nil_rated')

View file

@ -0,0 +1,27 @@
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.addons.l10n_in.tests.test_hsn_summary import TestL10nInHSNSummary
from odoo.addons.point_of_sale.tests.test_frontend import TestTaxCommonPOS
from odoo.tests import tagged
@tagged('post_install', '-at_install', 'post_install_l10n')
class TestL10nInHSNSummaryPos(TestTaxCommonPOS, TestL10nInHSNSummary):
@classmethod
@AccountTestInvoicingCommon.setup_country('in')
def setUpClass(cls):
super().setUpClass()
def create_base_line_product(self, base_line, **kwargs):
# OVERRIDE 'point_of_sale'
return super().create_base_line_product(base_line, **kwargs, l10n_in_hsn_code=base_line['l10n_in_hsn_code'])
def test_l10n_in_hsn_summary_pos(self):
# We only do the first test just to be sure the code is not crashing.
# There is no custom code in the POS for that so we suppose the results
# are exactly the same.
tests = self._test_l10n_in_hsn_summary_1()
test1 = next(tests)
self.ensure_products_on_document(test1[1], 'product_1')
with self.with_new_session(user=self.pos_user):
self.start_pos_tour('test_l10n_in_hsn_summary_pos')

View file

@ -0,0 +1,22 @@
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 TestGenericIN(TestGenericLocalization):
@classmethod
@AccountTestInvoicingCommon.setup_country('in')
def setUpClass(cls):
super().setUpClass()
cls.state_in_gj = cls.env.ref('base.state_in_gj')
cls.main_pos_config.company_id.write({
'name': "Default Company",
'state_id': cls.state_in_gj.id,
'vat': "24AAGCC7144L6ZE",
'street': "Khodiyar Chowk",
'street2': "Sala Number 3",
'city': "Amreli",
'zip': "365220",
})

View file

@ -0,0 +1,70 @@
from odoo.tests import tagged
from .common import TestInPosBase
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestPosFlow(TestInPosBase):
"""
Test class for GSTR-related POS functionality.
"""
def test_invoice_order_after_session_closed(self):
"""Test that an order can be invoiced after its session is closed.
Scenario:
1. Create a POS session and two orders:
- Order A: Not to be invoiced immediately.
- Order B: To be invoiced immediately.
2. Close the POS session.
3. Ensure:
- Both orders have `reversed_move_ids` unset.
4. Assign a partner to Order A and invoice it AFTER the session is closed.
- Confirm that `reversed_move_ids` is set accordingly.
"""
with self.with_pos_session():
# Create a POS order without an invoice, simulating basic sale and payment
pos_order_going_to_invoice = self._create_order({
'pos_order_lines_ui_args': [
(self.product_a, 2.0), # 2 units of product A
(self.product_b, 2.0), # 2 units of product B
],
'payments': [(self.bank_pm1, 630.0)], # Payment of 630 through bank payment method
})
# Create a POS order with an invoice and a linked customer
pos_order_with_invoice = self._create_order({
'pos_order_lines_ui_args': [
(self.product_a, 3.0), # 3 units of product A
(self.product_b, 3.0), # 3 units of product B
],
'payments': [(self.bank_pm1, 945.0)], # Payment of 945 through bank payment method
'customer': self.partner_a,
})
# Initial state: no reversal moves for both orders
self.assertFalse(pos_order_going_to_invoice.reversed_move_ids,
"Order with 'to_invoice' = False should have no reversal moves after session is closed.")
self.assertFalse(pos_order_with_invoice.reversed_move_ids,
"Immediate invoiced order should have no reversal moves after session is closed.")
# Now, set a partner and invoice the order after the session is closed
# with self.with_pos_session() as session2:
pos_order_going_to_invoice.partner_id = self.partner_a
pos_order_going_to_invoice.action_pos_order_invoice()
# session2.action_pos_session_closing_control()
# Confirm that reversal move(s) are now set
reversal_moves = self.env['account.move'].search([('reversed_pos_order_id', '=', pos_order_going_to_invoice.id)])
self.assertEqual(pos_order_going_to_invoice.reversed_move_ids, reversal_moves,
"Reversal move should be set for the order invoiced after the session is closed.")
self.assertInvoiceValues(reversal_moves, [
{'product_id': self.product_a.id, 'l10n_in_hsn_code': '1111', 'product_uom_id': self.product_a.uom_id.id, 'debit': 200.0, 'credit': 0.0},
{'product_id': self.product_b.id, 'l10n_in_hsn_code': '2222', 'product_uom_id': self.product_b.uom_id.id, 'debit': 400.0, 'credit': 0.0},
{'product_id': False, 'l10n_in_hsn_code': False, 'product_uom_id': False, 'debit': 15.0, 'credit': 0.0},
{'product_id': False, 'l10n_in_hsn_code': False, 'product_uom_id': False, 'debit': 15.0, 'credit': 0.0},
{'product_id': False, 'l10n_in_hsn_code': False, 'product_uom_id': False, 'debit': 0.0, 'credit': 630.0},
], {
'amount_untaxed': 0.0,
'amount_tax': 0.0,
'amount_total': 630.0,
})

View file

@ -0,0 +1,25 @@
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 TestGenericIN(TestGenericLocalization):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.main_pos_config.company_id.write({
'country_id': cls.env.ref("base.in")
})
def test_product_long_press_india(self):
""" Test the long press on product to open the product info """
self.main_pos_config.company_id.country_id.vat_label = 'Should stay GST even after editing vat_label'
self.env['product.product'].create({
'name': 'Test Product',
'list_price': 100,
'available_in_pos': True,
})
self.main_pos_config.with_user(self.pos_user).open_ui()
self.start_pos_tour('test_product_long_press_india')

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_pos_pos_form_inherit" model="ir.ui.view">
<field name="name">pos.order.form.inherit</field>
<field name="model">pos.order</field>
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='lines']/list/field[@name='product_id']" position="after">
<field name="l10n_in_hsn_code" optional="hide"/>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="l10n_in_pos.product_template_view_form_normalized_pos" model="ir.ui.view">
<field name="name">l10n_in_pos.product.template.view.form.normalized.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="point_of_sale.product_template_view_form_normalized_pos"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='pos_categ_ids']" position="before">
<field name="l10n_in_hsn_code" invisible="'IN' not in fiscal_country_codes or not l10n_in_is_gst_registered_enabled"/>
</xpath>
</field>
</record>
<record id="l10n_in_pos.product_tree_hsn_code" model="ir.ui.view">
<field name="name">l10n_in_pos.product.list.hsn_code.pos</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_product_tree_view"/>
<field name="arch" type="xml">
<field name="type" position="after">
<field name="l10n_in_hsn_code" string="HSN/SAC Code" optional="show"/>
</field>
</field>
</record>
<record id="action_missing_hsn_product" model="ir.actions.act_window">
<field name="name">Missing HSN Products</field>
<field name="res_model">product.product</field>
<field name="view_mode">kanban,list,form</field>
<field name="domain">[('l10n_in_hsn_missing_in_pos', '=', True)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new product variant
</p>
<p>
You must define an HSN for every product you sell through
the point of sale interface.
</p>
</field>
</record>
</odoo>

View file

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<odoo>
<record id="res_config_settings_view_form_l10n_in_pos_inherit" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.l10n_in_pos.view</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">
<xpath expr="//setting[@id='payment_methods_new']" position="before">
<div class="o_notification_alert alert alert-warning" role="alert" invisible="not is_kiosk_mode or country_code != 'IN'">
<span>Please note that the kiosk for INR currency only works with Razorpay terminal</span>
</div>
</xpath>
</field>
</record>
</odoo>

View file

@ -1,13 +1,15 @@
[project]
name = "odoo-bringout-oca-ocb-l10n_in_pos"
version = "16.0.0"
description = "Indian - Point of Sale - Odoo addon"
description = "Indian - Point of Sale -
Odoo addon
"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ocb-l10n_in>=16.0.0",
"odoo-bringout-oca-ocb-point_of_sale>=16.0.0",
"odoo-bringout-oca-ocb-l10n_in>=19.0.0",
"odoo-bringout-oca-ocb-point_of_sale>=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",
]