mirror of
https://github.com/bringout/oca-ocb-l10n_asia-pacific.git
synced 2026-04-25 01:42:06 +02:00
19.0 vanilla
This commit is contained in:
parent
7dc55599c6
commit
7f43bbbfcc
650 changed files with 45260 additions and 33436 deletions
|
|
@ -21,38 +21,18 @@ pip install odoo-bringout-oca-ocb-l10n_in
|
|||
|
||||
## Dependencies
|
||||
|
||||
This addon depends on:
|
||||
- account_tax_python
|
||||
- base_vat
|
||||
|
||||
## Manifest Information
|
||||
|
||||
- **Name**: Indian - Accounting
|
||||
- **Version**: 2.0
|
||||
- **Category**: Accounting/Localizations/Account Charts
|
||||
- **License**: LGPL-3
|
||||
- **Installable**: False
|
||||
- account_debit_note
|
||||
- account
|
||||
- iap
|
||||
|
||||
## Source
|
||||
|
||||
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `l10n_in`.
|
||||
- Repository: https://github.com/OCA/OCB
|
||||
- Branch: 19.0
|
||||
- Path: addons/l10n_in
|
||||
|
||||
## 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.
|
||||
|
|
|
|||
|
|
@ -3,3 +3,12 @@
|
|||
|
||||
from . import models
|
||||
from . import demo
|
||||
from . import wizard
|
||||
|
||||
def init_settings(env):
|
||||
# Activate cash rounding by default for all companies as soon as the module is installed.
|
||||
group_user = env.ref('base.group_user').sudo()
|
||||
group_user._apply_group(env.ref('account.group_cash_rounding'))
|
||||
|
||||
def post_init(env):
|
||||
init_settings(env)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Indian - Accounting',
|
||||
'website': 'https://www.odoo.com/documentation/latest/applications/finance/fiscal_localizations/india.html',
|
||||
'icon': '/account/static/description/l10n.png',
|
||||
'countries': ['in'],
|
||||
'version': '2.0',
|
||||
'description': """
|
||||
Indian Accounting: Chart of Account.
|
||||
|
|
@ -18,40 +19,61 @@ Sheet, now only Vertical format has been permitted Which is Supported By Odoo.
|
|||
""",
|
||||
'category': 'Accounting/Localizations/Account Charts',
|
||||
'depends': [
|
||||
'account_tax_python', 'base_vat',
|
||||
'account_tax_python',
|
||||
'base_vat',
|
||||
'account_debit_note',
|
||||
'account',
|
||||
'iap',
|
||||
],
|
||||
'auto_install': ['account'],
|
||||
'data': [
|
||||
'security/l10n_in_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/account_tax_group_data.xml',
|
||||
"data/iap_service_data.xml",
|
||||
'data/account.account.tag.csv',
|
||||
'data/l10n_in_chart_data.xml',
|
||||
'data/account.account.template.csv',
|
||||
'data/l10n_in_chart_post_data.xml',
|
||||
'data/account_tax_template_data.xml',
|
||||
'data/account_fiscal_position_data.xml',
|
||||
'data/l10n_in.port.code.csv',
|
||||
'data/res_country_state_data.xml',
|
||||
'data/res_country_group.xml',
|
||||
'data/uom_data.xml',
|
||||
'data/res_partner_industry.xml',
|
||||
'data/account_cash_rounding.xml',
|
||||
'data/account_tax_report_tcs_data.xml',
|
||||
'data/account_tax_report_tds_data.xml',
|
||||
'data/l10n_in.section.alert.csv',
|
||||
'wizard/l10n_in_withhold_wizard.xml',
|
||||
'views/l10n_in_pan_entity_views.xml',
|
||||
'views/l10n_in_section_alert_views.xml',
|
||||
'views/account_account_views.xml',
|
||||
'views/account_invoice_views.xml',
|
||||
'views/account_move_line_views.xml',
|
||||
'views/account_payment_views.xml',
|
||||
'views/account_journal_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/product_template_view.xml',
|
||||
'views/port_code_views.xml',
|
||||
'views/res_company_views.xml',
|
||||
'views/report_invoice.xml',
|
||||
'views/res_country_state_view.xml',
|
||||
'views/res_partner_views.xml',
|
||||
'views/account_tax_views.xml',
|
||||
'views/uom_uom_views.xml',
|
||||
'views/report_template.xml',
|
||||
'data/account_chart_template_data.xml',
|
||||
'report/audit_trail_report_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
'demo/demo_company.xml',
|
||||
'demo/res_partner_demo.xml',
|
||||
'demo/product_demo.xml',
|
||||
'demo/account_invoice_demo.xml',
|
||||
'demo/demo_company.xml',
|
||||
],
|
||||
'post_init_hook': 'post_init',
|
||||
'author': 'Odoo S.A.',
|
||||
'license': 'LGPL-3',
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'l10n_in/static/src/components/**/*',
|
||||
'l10n_in/static/src/helpers/*.js',
|
||||
],
|
||||
'web.assets_frontend': [
|
||||
'l10n_in/static/src/components/tests_shared_js_python/*',
|
||||
'l10n_in/static/src/helpers/*.js',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,16 @@
|
|||
"id","name","applicability"
|
||||
"tax_tag_base_sgst","BASE SGST","taxes"
|
||||
"tax_tag_base_cgst","BASE CGST","taxes"
|
||||
"tax_tag_base_igst","BASE IGST","taxes"
|
||||
"tax_tag_non_itc_base_sgst","NON ITC BASE SGST","taxes"
|
||||
"tax_tag_non_itc_base_cgst","NON ITC BASE CGST","taxes"
|
||||
"tax_tag_non_itc_base_igst","NON ITC BASE IGST","taxes"
|
||||
"tax_tag_other_non_itc_base_sgst","Other NON ITC BASE SGST","taxes"
|
||||
"tax_tag_other_non_itc_base_cgst","Other NON ITC BASE CGST","taxes"
|
||||
"tax_tag_other_non_itc_base_igst","Other NON ITC BASE IGST","taxes"
|
||||
"tax_tag_base_cess","BASE CESS","taxes"
|
||||
"tax_tag_base_state_cess","BASE STATE CESS","taxes"
|
||||
"tax_tag_non_itc_base_cess","NON ITC BASE CESS","taxes"
|
||||
"tax_tag_other_non_itc_base_cess","Other NON ITC BASE CESS","taxes"
|
||||
"tax_tag_exempt","EXEMPT","taxes"
|
||||
"tax_tag_nil_rated","NIL-RATED","taxes"
|
||||
"tax_tag_zero_rated","ZERO-RATED","taxes"
|
||||
"tax_tag_non_gst_supplies","NON GST SUPPLIES","taxes"
|
||||
"tax_tag_base_sgst_rc","BASE SGST (RC)","taxes"
|
||||
"tax_tag_base_cgst_rc","BASE CGST (RC)","taxes"
|
||||
"tax_tag_base_igst_rc","BASE IGST (RC)","taxes"
|
||||
"tax_tag_base_cess_rc","BASE CESS (RC)","taxes"
|
||||
"tax_tag_sgst","SGST","taxes"
|
||||
"tax_tag_cgst","CGST","taxes"
|
||||
"tax_tag_igst","IGST","taxes"
|
||||
"tax_tag_non_itc_sgst","NON ITC SGST","taxes"
|
||||
"tax_tag_non_itc_cgst","NON ITC CGST","taxes"
|
||||
"tax_tag_non_itc_igst","NON ITC IGST","taxes"
|
||||
"tax_tag_other_non_itc_sgst","Other NON ITC SGST","taxes"
|
||||
"tax_tag_other_non_itc_cgst","Other NON ITC CGST","taxes"
|
||||
"tax_tag_other_non_itc_igst","Other NON ITC IGST","taxes"
|
||||
"tax_tag_cess","CESS","taxes"
|
||||
"tax_tag_state_cess","STATE CESS","taxes"
|
||||
"tax_tag_non_itc_cess","NON ITC CESS","taxes"
|
||||
"tax_tag_other_non_itc_cess","Other NON ITC CESS","taxes"
|
||||
"tax_tag_sgst_rc","SGST (RC)","taxes"
|
||||
"tax_tag_cgst_rc","CGST (RC)","taxes"
|
||||
"tax_tag_igst_rc","IGST (RC)","taxes"
|
||||
"tax_tag_non_itc_sgst_rc","NON ITC SGST (RC)","taxes"
|
||||
"tax_tag_non_itc_cgst_rc","NON ITC CGST (RC)","taxes"
|
||||
"tax_tag_non_itc_igst_rc","NON ITC IGST (RC)","taxes"
|
||||
"tax_tag_other_non_itc_sgst_rc","Other NON ITC SGST (RC)","taxes"
|
||||
"tax_tag_other_non_itc_cgst_rc","Other NON ITC CGST (RC)","taxes"
|
||||
"tax_tag_other_non_itc_igst_rc","Other NON ITC IGST (RC)","taxes"
|
||||
"tax_tag_cess_rc","CESS (RC)","taxes"
|
||||
"tax_tag_non_itc_cess_rc","NON ITC CESS (RC)","taxes"
|
||||
"tax_tag_other_non_itc_cess_rc","Other NON ITC CESS (RC)","taxes"
|
||||
"id","name","applicability","country_id/id"
|
||||
"tax_tag_base_sgst","BASE SGST","taxes","base.in"
|
||||
"tax_tag_base_cgst","BASE CGST","taxes","base.in"
|
||||
"tax_tag_base_igst","BASE IGST","taxes","base.in"
|
||||
"tax_tag_base_cess","BASE CESS","taxes","base.in"
|
||||
"tax_tag_base_state_cess","BASE STATE CESS","taxes","base.in"
|
||||
"tax_tag_sgst","SGST","taxes","base.in"
|
||||
"tax_tag_cgst","CGST","taxes","base.in"
|
||||
"tax_tag_igst","IGST","taxes","base.in"
|
||||
"tax_tag_cess","CESS","taxes","base.in"
|
||||
"tax_tag_state_cess","STATE CESS","taxes","base.in"
|
||||
"tax_tag_non_itc","NON ITC","taxes","base.in"
|
||||
"tax_tag_other_non_itc","Other NON ITC","taxes","base.in"
|
||||
"tax_tag_eco_9_5","ECO 9(5)","taxes","base.in"
|
||||
"tax_tag_eco_tcs_52","ECO TCS 52","taxes","base.in"
|
||||
"account_tag_closing_stock","Closing Stock","accounts","base.in"
|
||||
|
|
|
|||
|
|
|
@ -1,69 +0,0 @@
|
|||
"id","name","code","account_type","chart_template_id/id","tag_ids/id","reconcile"
|
||||
"p10031","Inventories","10031","asset_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p10040","Debtors","10040","asset_receivable","l10n_in.indian_chart_template_standard","","True"
|
||||
"p10041","Debtors (PoS)","10041","asset_receivable","l10n_in.indian_chart_template_standard","","True"
|
||||
"p10051","SGST Receivable","10051","asset_current","l10n_in.indian_chart_template_standard","l10n_in.sgst_tag_account","False"
|
||||
"p10052","CGST Receivable","10052","asset_current","l10n_in.indian_chart_template_standard","l10n_in.cgst_tag_account","False"
|
||||
"p10053","IGST Receivable","10053","asset_current","l10n_in.indian_chart_template_standard","l10n_in.igst_tag_account","False"
|
||||
"p10057","Reverse Charge Tax Receivable","10057","asset_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p10054","TDS Receivable","10058","asset_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p10059","Tax Current Account - Receivable","10059","asset_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p10061","Deposit Account","10061","asset_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p10071","Prepaid Insurance","10071","asset_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1011","Buildings","1011","asset_fixed","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1012","Land","1012","asset_fixed","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1013","Equipments","1013","asset_fixed","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1014","Vehicle","1014","asset_fixed","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1015","Computer/Laptops (Assets)","1015","asset_fixed","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1016","Furniture","1016","asset_fixed","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1017","Air Conditionar","1017","asset_fixed","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1018","Misc Assets","1018","asset_fixed","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1111","Capital Account","1111","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p1112","Reserve And Surplus Account","1112","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p11211","Creditors","11211","liability_payable","l10n_in.indian_chart_template_standard","","True"
|
||||
"p11221","Bank OD Account","11221","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p11222","Secured Loan Account","11222","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p11223","Unsecured Loan Account","11223","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p11231","TDS Payable","11231","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p11232","SGST Payable","11232","liability_current","l10n_in.indian_chart_template_standard","l10n_in.sgst_tag_account","False"
|
||||
"p11233","CGST Payable","11233","liability_current","l10n_in.indian_chart_template_standard","l10n_in.cgst_tag_account","False"
|
||||
"p11234","IGST Payable","11234","liability_current","l10n_in.indian_chart_template_standard","l10n_in.igst_tag_account","False"
|
||||
"p11239","Tax Current Account - Payable","11239","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p11241","Wages Payable","11241","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p11242","Interest Payable","11242","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p11243","Notes Payable","11243","liability_current","l10n_in.indian_chart_template_standard","","False"
|
||||
"p20011","Local Sales","20011","income","l10n_in.indian_chart_template_standard","","False"
|
||||
"p20012","Retail Sales","20012","income","l10n_in.indian_chart_template_standard","","False"
|
||||
"p20013","Export Sales","20013","income","l10n_in.indian_chart_template_standard","","False"
|
||||
"p20021","Local Services","20021","income","l10n_in.indian_chart_template_standard","","False"
|
||||
"p20022","Export Services","20022","income","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2010","Interest Revenues","2010","income","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2011","Gain on Sale of Assets","2011","income","l10n_in.indian_chart_template_standard","","False"
|
||||
"2012","Write off Income","2012","income","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2013","Foreign Exchange Profit","2013","income_other","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2100","Electricity Expense","2100","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2101","Salary Expense","2101","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2102","Office Rent","2102","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2103","House Keeping Expense","2103","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2104","Postage And Courier Expense","2104","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2105","Internet Expense","2105","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2106","Telephone Expense","2106","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2107","Purchase Expense","2107","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2108","Computer/Laptop Accessories","2108","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2109","News Paper And Magazine","2109","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2110","Business Promotion","2110","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2111","Entertainment Expense","2111","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2112","Professional Services","2112","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2113","Bank Charges","2113","asset_cash","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2114","Diwali Bonus/Gift","2114","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2115","Parts Purchase","2115","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2116","Repairing Expense","2116","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2117","Foreign Exchange Loss","2117","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p21181","Sales Commission Expense","21181","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p21182","Stationary Expense","21182","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p21183","Travelling Expense","21183","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2121","Opening Stock","2121","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2122","Purchase Stock","2122","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2123","Closing Stock","2123","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2131","Loss on Sale of Assets","2131","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
"p2132","Write Off Expense","2132","expense","l10n_in.indian_chart_template_standard","","False"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="cash_rounding_in_half_up" model="account.cash.rounding">
|
||||
<field name="name">Half Up</field>
|
||||
<field name="rounding">1</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<function model="account.chart.template" name="try_loading">
|
||||
<value eval="[ref('l10n_in.indian_chart_template_standard')]"/>
|
||||
</function>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Fiscal Position Templates -->
|
||||
<record model="account.fiscal.position.template" id="fiscal_position_in_inter_state">
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="name">Inter State</field>
|
||||
</record>
|
||||
|
||||
<record model="account.fiscal.position.template" id="fiscal_position_in_export">
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="name">Export</field>
|
||||
</record>
|
||||
|
||||
<!-- Fiscal Position Tax Templates -->
|
||||
<record id="account_fiscal_position_tax_in_sale_1_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_1"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_1"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_2_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_2"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_2"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_5_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_5"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_5"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_12_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_12"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_12"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_18_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_18"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_18"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_28_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_28"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_28"/>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_tax_in_purchase_1_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_1"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_1"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_2_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_2"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_2"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_5_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_5"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_5"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_12_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_12"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_12"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_18_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_18"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_18"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_28_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_28"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_28"/>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_tax_in_sale_1_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_1"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_1"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_2_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_2"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_2"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_5_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_5"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_5"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_12_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_12"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_12"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_18_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_18"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_18"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_28_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_28"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_28"/>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_tax_in_purchase_1_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_1"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_1"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_2_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_2"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_2"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_5_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_5"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_5"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_12_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_12"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_12"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_18_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_18"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_18"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_28_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_28"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_28"/>
|
||||
</record>
|
||||
|
||||
<record model="account.fiscal.position.template" id="fiscal_position_in_reverse_charge_intra">
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="name">Reverse charge Intra State</field>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_tax_in_purchase_1_intra_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_intra"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_1"/>
|
||||
<field name="tax_dest_id" ref="sgst_purchase_1_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_2_intra_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_intra"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_2"/>
|
||||
<field name="tax_dest_id" ref="sgst_purchase_2_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_5_intra_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_intra"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_5"/>
|
||||
<field name="tax_dest_id" ref="sgst_purchase_5_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_12_intra_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_intra"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_12"/>
|
||||
<field name="tax_dest_id" ref="sgst_purchase_12_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_18_intra_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_intra"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_18"/>
|
||||
<field name="tax_dest_id" ref="sgst_purchase_18_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_28_intra_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_intra"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_28"/>
|
||||
<field name="tax_dest_id" ref="sgst_purchase_28_rc"/>
|
||||
</record>
|
||||
|
||||
<record model="account.fiscal.position.template" id="fiscal_position_in_reverse_charge_inter">
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="name">Reverse charge Inter State</field>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_tax_in_purchase_1_rc_inter_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_inter"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_1"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_1_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_2_rc_inter_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_inter"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_2"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_2_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_5_rc_inter_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_inter"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_5"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_5_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_12_rc_inter_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_inter"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_12"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_12_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_18_rc_inter_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_inter"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_18"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_18_rc"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_28_rc_inter_rc" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_reverse_charge_inter"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_28"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_28_rc"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="sgst_group" model="account.tax.group">
|
||||
<field name="name">SGST</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="cgst_group" model="account.tax.group">
|
||||
<field name="name">CGST</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="igst_group" model="account.tax.group">
|
||||
<field name="name">IGST</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="cess_group" model="account.tax.group">
|
||||
<field name="name">CESS</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="gst_group" model="account.tax.group">
|
||||
<field name="name">GST</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="exempt_group" model="account.tax.group">
|
||||
<field name="name">Exempt</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="nil_rated_group" model="account.tax.group">
|
||||
<field name="name">Nil Rated</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="non_gst_supplies_group" model="account.tax.group">
|
||||
<field name="name">Non GST Supplies</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo auto_sequence="1">
|
||||
<record id="tcs_report" model="account.report">
|
||||
<field name="name">TCS Report</field>
|
||||
<field name="root_report_id" ref="account.generic_tax_report"/>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
<field name="allow_foreign_vat" eval="True"/>
|
||||
<field name="availability_condition">country</field>
|
||||
<field name="column_ids">
|
||||
<record id="tcs_report_balance" model="account.report.column">
|
||||
<field name="name">Balance</field>
|
||||
<field name="expression_label">balance</field>
|
||||
</record>
|
||||
</field>
|
||||
<field name="line_ids">
|
||||
<record id="tcs_report_line_section_206c_1_alfhc" model="account.report.line">
|
||||
<field name="name">Section 206C(1): Alcoholic Liquor for human consumption</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1_alfhc_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1) Alcoholic Liquor</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1_tl" model="account.report.line">
|
||||
<field name="name">Section 206C(1): Tendu leaves</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1_tl_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1) Tendu leaves</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1_touafl" model="account.report.line">
|
||||
<field name="name">Section 206C(1): Timber obtained under a forest lease</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1_touafl_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1) Timber (forest lease)</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1_tobaotuafl" model="account.report.line">
|
||||
<field name="name">Section 206C(1): Timber obtained by any mode other than under a forest lease</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1_tobaotuafl_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1) Timber (other than under a forest lease)</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1_aofpnbtotl" model="account.report.line">
|
||||
<field name="name">Section 206C(1): Any other forest produce not being timber or tendu leaves</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1_aofpnbtotl_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1) other forest produce</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1_s" model="account.report.line">
|
||||
<field name="name">Section 206C(1): Scrap</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1_s_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1) Scrap</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1_mbcoloio" model="account.report.line">
|
||||
<field name="name">Section 206C(1): Minrals, being coal or lignite or iron ore</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1_mbcoloio_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1) Minrals</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1c_pl" model="account.report.line">
|
||||
<field name="name">Section 206C(1C): Parking lot</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1c_pl_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1C) Parking lot</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1c_tp" model="account.report.line">
|
||||
<field name="name">Section 206C(1C): Toll plaza</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1c_tp_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1C) Toll plaza</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1c_maq" model="account.report.line">
|
||||
<field name="name">Section 206C(1C): Mining and quarrying</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1c_maq_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1C) Mining and quarrying</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1f_mv" model="account.report.line">
|
||||
<field name="name">Section 206C(1F): Motor Vehicle</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1f_mv_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1F)</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1g_som" model="account.report.line">
|
||||
<field name="name">Section 206C(1G): Sum of money (above 7 lakhs) for remittance out of India</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1g_som_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1G) remittance out of India</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1g_soaotpp" model="account.report.line">
|
||||
<field name="name">Section 206C(1G): Seller of an overseas tour program package</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1g_soaotpp_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-206C(1G) overseas tour program</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tcs_report_line_section_206c_1h_sog" model="account.report.line">
|
||||
<field name="name">Section 206C(1H): Sale of Goods</field>
|
||||
<field name="hide_if_zero" eval="1"/>
|
||||
<field name="expression_ids">
|
||||
<record id="tcs_report_line_section_206c_1h_sog_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">206C(1H)</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,328 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo auto_sequence="1">
|
||||
<record id="tds_report" model="account.report">
|
||||
<field name="name">TDS Report</field>
|
||||
<field name="root_report_id" ref="account.generic_tax_report"/>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
<field name="allow_foreign_vat" eval="True"/>
|
||||
<field name="availability_condition">country</field>
|
||||
<field name="column_ids">
|
||||
<record id="tds_report_balance" model="account.report.column">
|
||||
<field name="name">Balance</field>
|
||||
<field name="expression_label">balance</field>
|
||||
</record>
|
||||
</field>
|
||||
<field name="line_ids">
|
||||
<record id="tds_report_line_section_192" model="account.report.line">
|
||||
<field name="name">Section 192: Payment of salary</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_192_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-192</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_192a" model="account.report.line">
|
||||
<field name="name">Section 192A: Payment of accumulated balance of provident fund which is taxable in the hands of an employee</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_192a_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-192A</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_193" model="account.report.line">
|
||||
<field name="name">Section 193: Interest on securities</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_193_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-193</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194" model="account.report.line">
|
||||
<field name="name">Section 194: Income by way of dividend</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194a" model="account.report.line">
|
||||
<field name="name">Section 194A: Income by way of interest other than "Interest on securities"</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194a_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194A</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194b" model="account.report.line">
|
||||
<field name="name">Section 194B: Income by way of winnings from lotteries, crossword puzzles, card games and other games of any sort</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194b_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194B</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194bb" model="account.report.line">
|
||||
<field name="name">Section 194BB: Income by way of winnings from horse races</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194bb_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194BB</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194c" model="account.report.line">
|
||||
<field name="name">Section 194C: Payment to contractor/sub-contractor</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194c_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194C</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194d" model="account.report.line">
|
||||
<field name="name">Section 194D: Insurance commission</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194d_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194D</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194da" model="account.report.line">
|
||||
<field name="name">Section 194DA: Payment in respect of life insurance policy</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194da_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194DA</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194e" model="account.report.line">
|
||||
<field name="name">Section 194E: Payment to non-resident sportsmen/sports association</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194e_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194E</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194ee" model="account.report.line">
|
||||
<field name="name">Section 194EE: Payment in respect of deposit under National Savings scheme</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194ee_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194EE</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194f" model="account.report.line">
|
||||
<field name="name">Section 194F: Payment on account of repurchase of unit by Mutual Fund or Unit Trust of India</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194f_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194F</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194g" model="account.report.line">
|
||||
<field name="name">Section 194G: Commission, etc., on sale of lottery tickets</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194g_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194G</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194h" model="account.report.line">
|
||||
<field name="name">Section 194H: Commission or brokerage</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194h_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194H</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194i" model="account.report.line">
|
||||
<field name="name">Section 194-I: Rent</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194i_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194I</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194ia" model="account.report.line">
|
||||
<field name="name">Section 194-IA: Payment on transfer of certain immovable property other than agricultural land</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194ia_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194IA</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194ib" model="account.report.line">
|
||||
<field name="name">Section 194-IB: Payment of rent by individual or HUF not liable to tax audit</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194ib_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194IB</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194ic" model="account.report.line">
|
||||
<field name="name">Section 194-IC: Payment of monetary consideration under Joint Development Agreements</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194ic_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194IC</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194j" model="account.report.line">
|
||||
<field name="name">Section 194J: Fees for professional or technical services</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194j_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194J</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194k" model="account.report.line">
|
||||
<field name="name">Section 194K: Income in respect of units payable to resident person</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194k_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194K</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194la" model="account.report.line">
|
||||
<field name="name">Section 194LA: Payment of compensation on acquisition of certain immovable property</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194la_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194LA</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194lba" model="account.report.line">
|
||||
<field name="name">Section 194LBA(1): Business trust shall deduct tax while distributing, any interest received or receivable by it from a SPV or any income received from renting or leasing or letting out any real estate asset owned directly by it, to its unit holders.</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194lba_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194LBA(1)</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194lb" model="account.report.line">
|
||||
<field name="name">Section 194LB: Payment of interest on infrastructure debt fund</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194lb_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194LB</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194lbb" model="account.report.line">
|
||||
<field name="name">Section 194LBB: Investment fund paying an income to a unit holder [other than income which is exempt under Section 10(23FBB)]</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194lbb_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194LBB</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194lbc" model="account.report.line">
|
||||
<field name="name">Section 194LBC: Income in respect of investment made in a securitisation trust (specified in Explanation of section115TCA)</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194lbc_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194LBC</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194m" model="account.report.line">
|
||||
<field name="name">Section 194M: Payment of commission (not being insurance commission), brokerage, contractual fee, professional fee to a resident person by an Individual or a HUF who are not liable to deduct TDS under section 194C, 194H, or 194J.</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194m_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194M</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194n" model="account.report.line">
|
||||
<field name="name">Section 194N: Cash withdrawal during the previous year from one or more account maintained by a person with a banking company, co-operative society engaged in business of banking or a post office</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194n_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194N</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194o" model="account.report.line">
|
||||
<field name="name">Section 194-O: Payment or credit of amount by the e-commerce operator to e-commerce participant</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194o_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194O</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_194q" model="account.report.line">
|
||||
<field name="name">Section 194Q: Purchase of goods</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_194q_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-194Q</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tds_report_line_section_195" model="account.report.line">
|
||||
<field name="name">Section 195: Payment of any other sum to a Non -resident</field>
|
||||
<field name="expression_ids">
|
||||
<record id="tds_report_line_section_195_tag" model="account.report.expression">
|
||||
<field name="label">balance</field>
|
||||
<field name="engine">tax_tags</field>
|
||||
<field name="formula">-195</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="iap_service_l10n_in_edi" model="iap.service">
|
||||
<field name="name">Indian EDI</field>
|
||||
<field name="technical_name">l10n_in_edi</field>
|
||||
<field name="description">Send electronic document to Indian government</field>
|
||||
<field name="unit_name">Credits</field>
|
||||
<field name="integer_balance">True</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
"id","name","consider_amount","is_per_transaction_limit","per_transaction_limit","is_aggregate_limit","aggregate_limit","aggregate_period","tax_source_type","tax_report_line_id/id"
|
||||
"tds_section_192","192","untaxed_amount","","","","","fiscal_yearly","tds","l10n_in.tds_report_line_section_192"
|
||||
"tds_section_192a","192A","untaxed_amount","","","True","50000","fiscal_yearly","tds","l10n_in.tds_report_line_section_192a"
|
||||
"tds_section_193","193","untaxed_amount","","","True","5000","fiscal_yearly","tds","l10n_in.tds_report_line_section_193"
|
||||
"tds_section_194","194","untaxed_amount","","","True","5000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194"
|
||||
"tds_section_194a","194A","untaxed_amount","","","True","5000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194a"
|
||||
"tds_section_194b","194B","untaxed_amount","","","True","10000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194b"
|
||||
"tds_section_194ba","194BA","untaxed_amount","","","True","0","fiscal_yearly","tds",""
|
||||
"tds_section_194bb","194BB","untaxed_amount","","","True","10000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194bb"
|
||||
"tds_section_194c","194C","untaxed_amount","True","30000","True","100000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194c"
|
||||
"tds_section_194d","194D","untaxed_amount","","","True","15000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194d"
|
||||
"tds_section_194da","194DA","untaxed_amount","","","True","100000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194da"
|
||||
"tds_section_194e","194E","untaxed_amount","","","True","0","fiscal_yearly","tds","l10n_in.tds_report_line_section_194e"
|
||||
"tds_section_194ee","194EE","untaxed_amount","","","True","2500","fiscal_yearly","tds","l10n_in.tds_report_line_section_194ee"
|
||||
"tds_section_194f","194F","untaxed_amount","","","True","0","fiscal_yearly","tds","l10n_in.tds_report_line_section_194f"
|
||||
"tds_section_194g","194G","untaxed_amount","","","True","15000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194g"
|
||||
"tds_section_194h","194H","untaxed_amount","","","True","15000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194h"
|
||||
"tds_section_194i","194I","untaxed_amount","","","True","240000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194i"
|
||||
"tds_section_194ia","194IA","untaxed_amount","True","5000000","","","fiscal_yearly","tds","l10n_in.tds_report_line_section_194ia"
|
||||
"tds_section_194ib","194IB","untaxed_amount","","","True","50000","monthly","tds","l10n_in.tds_report_line_section_194ib"
|
||||
"tds_section_194ic","194IC","untaxed_amount","","","True","0","fiscal_yearly","tds","l10n_in.tds_report_line_section_194ic"
|
||||
"tds_section_194j","194J","untaxed_amount","","","True","30000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194j"
|
||||
"tds_section_194j_dir","194J(DIRECTORS)","untaxed_amount","","","True","0","fiscal_yearly","tds",""
|
||||
"tds_section_194k","194K","untaxed_amount","","","True","5000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194k"
|
||||
"tds_section_194la","194LA","untaxed_amount","","","True","250000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194la"
|
||||
"tds_section_194lba1","194LBA(1)","untaxed_amount","","","True","0","fiscal_yearly","tds","l10n_in.tds_report_line_section_194lba"
|
||||
"tds_section_194lbb","194LBB","untaxed_amount","","","True","0","fiscal_yearly","tds","l10n_in.tds_report_line_section_194lbb"
|
||||
"tds_section_194lb","194LB","untaxed_amount","","","True","0","fiscal_yearly","tds","l10n_in.tds_report_line_section_194lb"
|
||||
"tds_section_194lbc","194LBC","untaxed_amount","","","True","0","fiscal_yearly","tds","l10n_in.tds_report_line_section_194lbc"
|
||||
"tds_section_194m","194M","untaxed_amount","","","True","5000000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194m"
|
||||
"tds_section_194n","194N","untaxed_amount","","","True","10000000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194n"
|
||||
"tds_section_194o_huf","194O(HUF)","untaxed_amount","","","True","500000","fiscal_yearly","tds",""
|
||||
"tds_section_194o","194O","untaxed_amount","","","True","0","fiscal_yearly","tds","l10n_in.tds_report_line_section_194o"
|
||||
"tds_section_194q","194Q","untaxed_amount","","","True","5000000","fiscal_yearly","tds","l10n_in.tds_report_line_section_194q"
|
||||
"tds_section_195","195","untaxed_amount","","","True","0","fiscal_yearly","tds","l10n_in.tds_report_line_section_195"
|
||||
"tcs_section_206c1_alc","206C(1) Liquor","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1_alfhc"
|
||||
"tcs_section_206c1_tl","206C(1) Tendu leaves","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1_tl"
|
||||
"tcs_section_206c1_tim","206C(1) Timber woods(FL)","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1_touafl"
|
||||
"tcs_section_206c1_tim_o","206C(1) Timber woods","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1_tobaotuafl"
|
||||
"tcs_section_206c1_fo","206C(1) OFP","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1_aofpnbtotl"
|
||||
"tcs_section_206c1_sc","206C(1) Scrap","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1_s"
|
||||
"tcs_section_206c1_min","206C(1) Min","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1_mbcoloio"
|
||||
"tcs_section_206c1c_p","206C(1C) Parking lot","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1c_pl"
|
||||
"tcs_section_206c1c_t","206C(1C) Toll plaza","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1c_tp"
|
||||
"tcs_section_206c1c_mq","206C(1C) MQ","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1c_maq"
|
||||
"tcs_section_206c1f_mv","206C(1F) Motor Vehicle","total_amount","True","1000000","","","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1f_mv"
|
||||
"tcs_section_206c1g_r","206C(1G) Remittance","total_amount","","","True","700000","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1g_som"
|
||||
"tcs_section_206c1g_ot","206C(1G) Overseas Tour","total_amount","","","True","0","fiscal_yearly","tcs","l10n_in.tcs_report_line_section_206c_1g_soaotpp"
|
||||
|
|
|
@ -1,61 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<menuitem id="account_reports_in_statements_menu" name="India" parent="account.menu_finance_reports" sequence="5"/>
|
||||
|
||||
<record id="indian_chart_template_standard" model="account.chart.template">
|
||||
<field name="name">Indian Chart of Accounts - Standard</field>
|
||||
<field name="bank_account_code_prefix">1002</field>
|
||||
<field name="cash_account_code_prefix">1001</field>
|
||||
<field name="transfer_account_code_prefix">1008</field>
|
||||
<field name="code_digits">6</field>
|
||||
<field name="currency_id" ref="base.INR"/>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<menuitem id="account_reports_in_statements_menu" name="India" parent="account.menu_finance_reports" sequence="6" groups="account.group_account_readonly"/>
|
||||
|
||||
<record id="sgst_tag_account" model="account.account.tag">
|
||||
<field name="name">SGST</field>
|
||||
<field name="applicability">accounts</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="cgst_tag_account" model="account.account.tag">
|
||||
<field name="name">CGST</field>
|
||||
<field name="applicability">accounts</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="igst_tag_account" model="account.account.tag">
|
||||
<field name="name">IGST</field>
|
||||
<field name="applicability">accounts</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="cess_tag_account" model="account.account.tag">
|
||||
<field name="name">CESS</field>
|
||||
<field name="applicability">accounts</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
</record>
|
||||
<record id="p10055" model="account.account.template">
|
||||
<field name="name">CESS Receivable</field>
|
||||
<field name="code">10055</field>
|
||||
<field name="account_type">asset_current</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('cess_tag_account'),])]"/>
|
||||
</record>
|
||||
<record id="p10056" model="account.account.template">
|
||||
<field name="name">Tax Receivable</field>
|
||||
<field name="code">10056</field>
|
||||
<field name="account_type">asset_current</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p11235">
|
||||
<field name="name">CESS Payable</field>
|
||||
<field name="code">11235</field>
|
||||
<field name="account_type">liability_current</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('cess_tag_account'),])]"/>
|
||||
</record>
|
||||
<record id="p11236" model="account.account.template">
|
||||
<field name="name">Tax Payable</field>
|
||||
<field name="code">11236</field>
|
||||
<field name="account_type">liability_current</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
</odoo>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="indian_chart_template_standard" model="account.chart.template">
|
||||
<field name="property_account_receivable_id" ref="p10040"/>
|
||||
<field name="property_account_payable_id" ref="p11211"/>
|
||||
<field name="property_account_expense_categ_id" ref="p2107"/>
|
||||
<field name="property_account_income_categ_id" ref="p20011"/>
|
||||
<field name="property_tax_payable_account_id" ref="p11239"/>
|
||||
<field name="property_tax_receivable_account_id" ref="p10059"/>
|
||||
<field name="income_currency_exchange_account_id" ref="p2013"/>
|
||||
<field name="expense_currency_exchange_account_id" ref="p2117"/>
|
||||
<field name="default_pos_receivable_account_id" ref="p10041"/>
|
||||
<field name="account_journal_early_pay_discount_loss_account_id" ref="p2132"/>
|
||||
<field name="account_journal_early_pay_discount_gain_account_id" ref="2012"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
UPDATE res_company
|
||||
SET l10n_in_edi_production_env = false;
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="inter_state_group" model="res.country.group">
|
||||
<field name="name">India Inter-State Group</field>
|
||||
<field name="code">IN-INTER</field>
|
||||
<field name="country_ids" eval="[Command.set([ref('base.in')])]"/>
|
||||
<field name="exclude_state_ids" eval="[Command.set([ref('l10n_in.state_in_oc')])]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="eco_under_section_52" model="res.partner.industry">
|
||||
<field name="name">ECO liable to deduct TCS u/s 52</field>
|
||||
<field name="full_name">E-Commerce operator liable to deduct TCS under section 52</field>
|
||||
</record>
|
||||
<record id="eco_under_section_9_5" model="res.partner.industry">
|
||||
<field name="name">ECO liable to pay GST u/s 9(5)</field>
|
||||
<field name="full_name">E-Commerce operator liable to pay tax under section 9(5)</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
"id","name","code","account_type","tag_ids","reconcile","non_trade","l10n_in_tds_tcs_section_id"
|
||||
"p10031","Inventories","10031","asset_current","","False","",""
|
||||
"p10040","Debtors","10040","asset_receivable","","True","",""
|
||||
"p10041","Debtors (PoS)","10041","asset_receivable","","True","",""
|
||||
"p10051","SGST Receivable","10051","asset_current","l10n_in.sgst_tag_account","False","",""
|
||||
"p10052","CGST Receivable","10052","asset_current","l10n_in.cgst_tag_account","False","",""
|
||||
"p10053","IGST Receivable","10053","asset_current","l10n_in.igst_tag_account","False","",""
|
||||
"p100531","IGST Paid on SEZ/Export Sales","100531","asset_current","l10n_in.igst_tag_account","False","",""
|
||||
"p10054","IGST SEZ/Export Control Account","10054","asset_current","","False","",""
|
||||
"p10057","Reverse Charge GST on Purchase","10057","asset_current","","False","",""
|
||||
"p10058","TDS Receivable","10058","asset_current","","False","",""
|
||||
"p10059","Tax Current Account - Receivable","10059","asset_receivable","","True","True",""
|
||||
"p10061","Deposit Account","10061","asset_current","","False","",""
|
||||
"p10071","Prepaid Insurance","10071","asset_current","","False","",""
|
||||
"p1011","Buildings","1011","asset_fixed","","False","",""
|
||||
"p1012","Land","1012","asset_fixed","","False","",""
|
||||
"p1013","Equipment","1013","asset_fixed","","False","",""
|
||||
"p1014","Vehicle","1014","asset_fixed","","False","",""
|
||||
"p1015","Computer/Laptops (Assets)","1015","asset_fixed","","False","",""
|
||||
"p1016","Furniture","1016","asset_fixed","","False","",""
|
||||
"p1017","Air Conditionar","1017","asset_fixed","","False","",""
|
||||
"p1018","Misc Assets","1018","asset_fixed","","False","",""
|
||||
"p1111","Capital Account","1111","equity","","False","",""
|
||||
"p1112","Reserve And Surplus Account","1112","liability_current","","False","",""
|
||||
"p11211","Creditors","11211","liability_payable","","True","",""
|
||||
"p11221","Bank OD Account","11221","liability_current","","False","",""
|
||||
"p11222","Secured Loan Account","11222","liability_current","","False","",""
|
||||
"p11223","Unsecured Loan Account","11223","liability_current","","False","",""
|
||||
"p11231","TDS Payable","11231","liability_current","","False","",""
|
||||
"p11232","SGST Payable","11232","liability_current","l10n_in.sgst_tag_account","False","",""
|
||||
"p11233","CGST Payable","11233","liability_current","l10n_in.cgst_tag_account","False","",""
|
||||
"p11234","IGST Payable","11234","liability_current","l10n_in.igst_tag_account","False","",""
|
||||
"p112341","IGST SEZ/Export Control Account","112341","liability_current","","False","",""
|
||||
"p112342","IGST Payable - Export","112342","liability_current","l10n_in.igst_tag_account","False","",""
|
||||
"p11237","GST RCM Control Account","11237","liability_current","","False","",""
|
||||
"p11239","Tax Current Account - Payable","11239","liability_payable","","True","True",""
|
||||
"p11241","Wages Payable","11241","liability_current","","False","",""
|
||||
"p11242","Interest Payable","11242","liability_current","","False","",""
|
||||
"p11243","Notes Payable","11243","liability_current","","False","",""
|
||||
"p20011","Local Sales","20011","income","","False","",""
|
||||
"p20012","Retail Sales","20012","income","","False","",""
|
||||
"p20013","Export Sales","20013","income","","False","",""
|
||||
"p20021","Local Services","20021","income","","False","",""
|
||||
"p20022","Export Services","20022","income","","False","",""
|
||||
"p2010","Interest Revenues","2010","income","","False","",""
|
||||
"p2011","Gain on Sale of Assets","2011","income","","False","",""
|
||||
"2012","Write off Income","2012","income","","False","",""
|
||||
"p2013","Foreign Exchange Profit","2013","income_other","","False","",""
|
||||
"p2100","Electricity Expense","2100","expense","","False","",""
|
||||
"p2101","Salary Expense","2101","expense","","False","",""
|
||||
"p2102","Office Rent","2102","expense","","False","",""
|
||||
"p2103","House Keeping Expense","2103","expense","","False","",""
|
||||
"p2104","Postage And Courier Expense","2104","expense","","False","",""
|
||||
"p2105","Internet Expense","2105","expense","","False","",""
|
||||
"p2106","Telephone Expense","2106","expense","","False","",""
|
||||
"p2107","Purchase Expense","2107","expense","","False","",""
|
||||
"p2108","Computer/Laptop Accessories","2108","expense","","False","",""
|
||||
"p2109","News Paper And Magazine","2109","expense","","False","",""
|
||||
"p2110","Business Promotion","2110","expense","","False","",""
|
||||
"p2111","Entertainment Expense","2111","expense","","False","",""
|
||||
"p2112","Professional Services","2112","expense","","False","",""
|
||||
"p2113","Bank Charges","2113","expense","","False","",""
|
||||
"p2114","Diwali Bonus/Gift","2114","expense","","False","",""
|
||||
"p2115","Parts Purchase","2115","expense","","False","",""
|
||||
"p2116","Repairing Expense","2116","expense","","False","",""
|
||||
"p2117","Foreign Exchange Loss","2117","expense","","False","",""
|
||||
"p21181","Sales Commission Expense","21181","expense","","False","",""
|
||||
"p21182","Stationary Expense","21182","expense","","False","",""
|
||||
"p21183","Travelling Expense","21183","expense","","False","",""
|
||||
"p2121","Opening Stock","2121","expense","","False","",""
|
||||
"p2122","Purchase Stock","2122","expense","","False","",""
|
||||
"p2123","Closing Stock","2123","expense","l10n_in.account_tag_closing_stock","False","",""
|
||||
"p2131","Loss on Sale of Assets","2131","expense","","False","",""
|
||||
"p2132","Write Off Expense","2132","expense","","False","",""
|
||||
"p213201","Round off Expense","213201","expense","","False","",""
|
||||
"p213202","Round off Income","213202","income","","False","",""
|
||||
"p213300","Depreciation Account","213300","expense","","False","",""
|
||||
"p11244","TDS Deducted","11244","liability_current","","False","",""
|
||||
"p11245","TCS Collected","11245","liability_current","","False","",""
|
||||
"p10055","CESS Receivable","10055","asset_current","l10n_in.cess_tag_account","False","",""
|
||||
"p10056","Tax Receivable","10056","asset_current","","False","",""
|
||||
"p11235","CESS Payable","11235","liability_current","l10n_in.cess_tag_account","False","",""
|
||||
"p11236","Tax Payable","11236","liability_current","","False","",""
|
||||
"p10084","Deferred Expenses","10084","asset_current","","False","",""
|
||||
"p10085","Deferred Income","10085","liability_current","","False","",""
|
||||
"p300001","House Rent Allowance Expense","300001","expense","","False","",""
|
||||
"p300002","Other Allowance Expense","300002","expense","","False","",""
|
||||
"p300003","Bonus to Employee Expense","300003","expense","","False","",""
|
||||
"p300004","Supplementary Allowance Expense","300004","liability_current","","False","",""
|
||||
"p300005","Performance Bonus","300005","liability_current","","False","",""
|
||||
"p300006","Employee Reimbursement Expense","300006","liability_payable","","True","",""
|
||||
"p300007","Provident fund - Employee Payable","300007","liability_current","","False","",""
|
||||
"p300008","Provident fund - Employer Payable","300008","liability_current","","False","",""
|
||||
"p300009","Advance to Employee","300009","liability_current","","False","",""
|
||||
"p300010","Salary Exp Payable","300010","liability_current","","True","",""
|
||||
"p300011","Leave Travel Allowance Expense","300011","liability_current","","False","",""
|
||||
"p300012","Professional Tax Payable","300012","liability_current","","False","",""
|
||||
"p100595","TDS (Withholding Control)","100595","asset_current","","False","",""
|
||||
"p211210","Professional Fees","211210","expense","","False","","l10n_in.tds_section_194j"
|
||||
"p211220","Audit Fees","211220","expense","","False","","l10n_in.tds_section_194j"
|
||||
"p211230","Job Work Expense","211230","expense","","False","","l10n_in.tds_section_194c"
|
||||
"p211240","Advertisement Expense","211240","expense","","False","","l10n_in.tds_section_194c"
|
||||
"p211250","Commission/Brokerage Expense","211250","expense","","False","","l10n_in.tds_section_194h"
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
"id","name","note","sequence"
|
||||
"fiscal_position_in_reverse_charge_intra","Reverse charge Intra State","THE SUPPLY IS SUBJECT TO REVERSE CHARGE MECHANISM, SO THE RECIPIENT IS RESPONSIBLE FOR PAYING TAX.","10"
|
||||
"fiscal_position_in_reverse_charge_inter","Reverse charge Inter State","THE SUPPLY IS SUBJECT TO REVERSE CHARGE MECHANISM, SO THE RECIPIENT IS RESPONSIBLE FOR PAYING TAX.","20"
|
||||
|
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,11 @@
|
|||
"id","name","country_id","sequence","tax_payable_account_id","tax_receivable_account_id"
|
||||
"sgst_group","SGST/UTGST","base.in","","p11239","p10059"
|
||||
"cgst_group","CGST","base.in","","p11239","p10059"
|
||||
"igst_group","IGST","base.in","","p11239","p10059"
|
||||
"cess_group","CESS","base.in","","p11239","p10059"
|
||||
"gst_group","GST","base.in","","p11239","p10059"
|
||||
"exempt_group","Exempt","base.in","","p11239","p10059"
|
||||
"nil_rated_group","Nil Rated","base.in","","p11239","p10059"
|
||||
"non_gst_supplies_group","Non GST Supplies","base.in","","p11239","p10059"
|
||||
"tcs_group","TCS","base.in","100","p11239","p10059"
|
||||
"tds_group","TDS","base.in","100","p11239","p10059"
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<record id="uom.product_uom_cm" model="uom.uom">
|
||||
<field name="l10n_in_code">CMS-CENTIMETERS</field>
|
||||
</record>
|
||||
<record id="uom.uom_square_meter" model="uom.uom">
|
||||
<record id="uom.product_uom_square_meter" model="uom.uom">
|
||||
<field name="l10n_in_code">SQM-SQUARE METERS</field>
|
||||
</record>
|
||||
<record id="uom.product_uom_litre" model="uom.uom">
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
<record id="uom.product_uom_mile" model="uom.uom">
|
||||
<field name="l10n_in_code">OTH-OTHERS</field>
|
||||
</record>
|
||||
<record id="uom.uom_square_foot" model="uom.uom">
|
||||
<record id="uom.product_uom_square_foot" model="uom.uom">
|
||||
<field name="l10n_in_code">SQF-SQUARE FEET</field>
|
||||
</record>
|
||||
<record id="uom.product_uom_floz" model="uom.uom">
|
||||
|
|
@ -81,4 +81,7 @@
|
|||
<record id="uom.product_uom_cubic_foot" model="uom.uom">
|
||||
<field name="l10n_in_code">OTH-OTHERS</field>
|
||||
</record>
|
||||
<record id="uom.product_uom_milliliter" model="uom.uom">
|
||||
<field name="l10n_in_code">MLT-MILILITRE</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,623 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import api, models
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from odoo import api, models, Command
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools.misc import file_open
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccountChartTemplate(models.AbstractModel):
|
||||
_inherit = "account.chart.template"
|
||||
|
||||
@api.model
|
||||
def _get_demo_data(self):
|
||||
company = self.env.company
|
||||
def _get_demo_data(self, company=False):
|
||||
demo_data = {}
|
||||
if company.account_fiscal_country_id.code == "IN":
|
||||
if company.state_id.country_id.code != "IN":
|
||||
company.state_id = self.env.ref("base.state_in_gj")
|
||||
if company.country_id.code != "IN":
|
||||
company.country_id = self.env.ref("base.in")
|
||||
return super()._get_demo_data()
|
||||
if company.state_id:
|
||||
company.write({
|
||||
'l10n_in_is_gst_registered': True,
|
||||
'l10n_in_tcs_feature': True,
|
||||
'l10n_in_tds_feature': True,
|
||||
'l10n_in_edi_production_env': False,
|
||||
})
|
||||
demo_data = {
|
||||
'res.partner.category': self._get_demo_data_res_partner_category(company),
|
||||
'res.partner': self._get_demo_data_partner(),
|
||||
'account.move': self._get_demo_data_move(company),
|
||||
'res.config.settings': self._get_demo_data_config_settings(company),
|
||||
'ir.attachment': self._get_demo_data_attachment(company),
|
||||
'mail.message': self._get_demo_data_mail_message(company),
|
||||
}
|
||||
else:
|
||||
_logger.warning('Error while loading Indian-Accounting demo data in the company "%s".State is not set in the company.', company.name)
|
||||
else:
|
||||
demo_data = super()._get_demo_data(company)
|
||||
return demo_data
|
||||
|
||||
@api.model
|
||||
def _get_demo_data_config_settings(self, company=False):
|
||||
return{
|
||||
'sales_credit_limit':{
|
||||
'account_use_credit_limit': True,
|
||||
'account_default_credit_limit': '10000'
|
||||
}
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _get_demo_data_res_partner_category(self, company=False):
|
||||
return{
|
||||
'res_partner_category_registered': {
|
||||
'name': 'Registered',
|
||||
'color': 2,
|
||||
},
|
||||
'res_partner_category_unregistered': {
|
||||
'name': 'Unregistered',
|
||||
'color': 3,
|
||||
},
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _get_demo_data_partner(self):
|
||||
company = self.env.company
|
||||
if company.account_fiscal_country_id.code != "IN" or not company.state_id:
|
||||
return super()._get_demo_data_partner()
|
||||
inter_state_ref = 'base.state_in_ts'
|
||||
intra_state_ref = 'base.state_in_gj'
|
||||
default_partner_dict = {'country_id': 'base.in', 'is_company': True, 'company_id': company.id}
|
||||
return{
|
||||
'res_partner_registered_customer': {
|
||||
**default_partner_dict,
|
||||
'name': 'B2B Customer Intra State',
|
||||
'category_id': 'res_partner_category_registered',
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'street': '201, Second Floor, IT Tower 4',
|
||||
'street2': 'InfoCity Gate - 1, Infocity',
|
||||
'city': 'Gandhinagar',
|
||||
'state_id': 'base.state_in_gj',
|
||||
'zip': '382010',
|
||||
'vat': '24AABCT1332L2ZD',
|
||||
},
|
||||
'res_partner_registered_customer_inter_state': {
|
||||
**default_partner_dict,
|
||||
'name': 'B2B Customer Inter State',
|
||||
'category_id': 'res_partner_category_registered',
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'street': 'floor-1, Maddikunta-Ankanpally Village',
|
||||
'street2': 'Post box No 2, NH-65',
|
||||
'city': 'Hyderabad',
|
||||
'state_id': inter_state_ref,
|
||||
'zip': '500014',
|
||||
'vat': '36AAACM4154G1ZO',
|
||||
},
|
||||
'res_partner_unregistered_customer':{
|
||||
**default_partner_dict,
|
||||
'name': 'B2C Customer Intra State',
|
||||
'category_id': 'res_partner_category_unregistered',
|
||||
'l10n_in_gst_treatment': 'unregistered',
|
||||
'street': 'B105, yogeshwar Tower',
|
||||
'state_id': intra_state_ref,
|
||||
'city': 'Rajkot',
|
||||
'zip': '360001'
|
||||
},
|
||||
'res_partner_unregistered_customer_inter_state':{
|
||||
**default_partner_dict,
|
||||
'name': 'B2C Customer Inter State',
|
||||
'category_id': 'res_partner_category_unregistered',
|
||||
'l10n_in_gst_treatment': 'unregistered',
|
||||
'street': '80, Sarojini Devi Road',
|
||||
'city': 'Hyderabad',
|
||||
'state_id': inter_state_ref,
|
||||
'zip': '500003'
|
||||
},
|
||||
'res_partner_registered_supplier_1': {
|
||||
**default_partner_dict,
|
||||
'name': 'Supplier',
|
||||
'category_id': 'res_partner_category_registered',
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'street': '19, Ground Floor',
|
||||
'street2': 'Survey Road,Vadipatti',
|
||||
'city': 'Madurai',
|
||||
'state_id': 'base.state_in_tn',
|
||||
'zip': '625218',
|
||||
'vat': '33AACCT6304M1DB',
|
||||
},
|
||||
'res_partner_registered_supplier_2': {
|
||||
**default_partner_dict,
|
||||
'name': 'Odoo In Private Limited',
|
||||
'category_id': 'res_partner_category_registered',
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'street': '401, Fourth Floor, IT Tower 4',
|
||||
'street2': 'InfoCity Gate - 1, Infocity',
|
||||
'city': 'Hyderabad',
|
||||
'state_id': inter_state_ref,
|
||||
'zip': '500014',
|
||||
'vat': '36AACCT6304M1ZB',
|
||||
},
|
||||
'res_partner_overseas': {
|
||||
'name': 'Supplier Overseas',
|
||||
'l10n_in_gst_treatment': 'overseas',
|
||||
'street': '142 Street, Rigas building',
|
||||
'street2': 'Survey Road,',
|
||||
'city': 'City',
|
||||
'zip': '000000',
|
||||
'state_id': 'base.state_us_5',
|
||||
'country_id': 'base.us',
|
||||
'is_company': True,
|
||||
'company_id': company.id,
|
||||
},
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _get_demo_data_move(self, company=False):
|
||||
cid = company.id or self.env.company.id
|
||||
def _get_tax_by_id(tax_id):
|
||||
tax = self.env.ref('account.%s_%s'%((cid), (tax_id)))
|
||||
return tax.id
|
||||
if company.account_fiscal_country_id.code == "IN":
|
||||
sale_journal = self.env['account.journal'].search(
|
||||
domain=[
|
||||
*self.env['account.journal']._check_company_domain(cid),
|
||||
('type', '=', 'sale'),
|
||||
], limit=1)
|
||||
return {
|
||||
# Demo of B2B (business-to-business) Taxable supplies made to other registered person.
|
||||
self.company_xmlid('demo_invoice_b2b_1'): {
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': 'res_partner_registered_customer',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'journal_id': sale_journal.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_8',
|
||||
'quantity': 2,
|
||||
'price_unit': 40000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_sale_28')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_9',
|
||||
'quantity': 3,
|
||||
'price_unit': 400.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_sale_28'), _get_tax_by_id('cess_5_plus_1591_sale')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_10',
|
||||
'quantity': 4,
|
||||
'price_unit': 300.0,
|
||||
'tax_ids':[Command.set([_get_tax_by_id('sgst_sale_18')])],
|
||||
}),
|
||||
],
|
||||
},
|
||||
self.company_xmlid('demo_invoice_b2b_2'): {
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': 'res_partner_registered_customer_inter_state',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'journal_id': sale_journal.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_9',
|
||||
'quantity': 2,
|
||||
'price_unit': 4000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_sale_5')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_10',
|
||||
'quantity': 3,
|
||||
'price_unit': 300.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_sale_5')])],
|
||||
}),
|
||||
],
|
||||
},
|
||||
self.company_xmlid('demo_bill_b2b_1'): {
|
||||
'ref': 'INV/001',
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': 'res_partner_registered_supplier_2',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_01',
|
||||
'quantity': 1,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_purchase_18')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_03',
|
||||
'quantity': 1,
|
||||
'price_unit': 2000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_purchase_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
self.company_xmlid('demo_bill_b2b_2'): {
|
||||
'ref': 'INV/002',
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': 'res_partner_registered_supplier_2',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_01',
|
||||
'quantity': 4,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_purchase_18')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_03',
|
||||
'quantity': 3,
|
||||
'price_unit': 2000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_purchase_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
self.company_xmlid('demo_bill_b2b_3'): {
|
||||
'ref': 'INV/003',
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': 'res_partner_registered_supplier_1',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_01',
|
||||
'quantity': 2,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_purchase_18')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_03',
|
||||
'quantity': 3,
|
||||
'price_unit': 2000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_purchase_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
self.company_xmlid('demo_invoice_to_extract'): {
|
||||
'move_type': 'in_invoice',
|
||||
'message_main_attachment_id': 'ir_attachment_in_invoice_1',
|
||||
},
|
||||
self.company_xmlid('demo_invoice_service'): {
|
||||
'ref': 'MYS-91021146',
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': 'res_partner_registered_supplier_2',
|
||||
'invoice_user_id': False,
|
||||
'invoice_date': datetime.now(),
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'name': 'Integrated Managed Infrastructure Service',
|
||||
'quantity': 1,
|
||||
'price_unit': 69132.78,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_purchase_18')])],
|
||||
}),
|
||||
],
|
||||
'message_main_attachment_id': 'ir_attachment_in_invoice_2',
|
||||
},
|
||||
# Demo of IMP(Import) of supplies.
|
||||
self.company_xmlid('demo_bill_imp'): {
|
||||
'ref': 'BOE/123',
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': 'res_partner_overseas',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_4',
|
||||
'quantity': 30,
|
||||
'price_unit': 9000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_purchase_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
# Demo of cdnr(Credit/ Debit Note for registered business). Create credit note for demo b2b bill.
|
||||
self.company_xmlid('demo_bill_cdnr_1'): {
|
||||
'ref': 'CR/001',
|
||||
'move_type': 'in_refund',
|
||||
'partner_id': 'res_partner_registered_supplier_2',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now() - timedelta(days=1),
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_01',
|
||||
'quantity': 1,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_purchase_18')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_03',
|
||||
'quantity': 1,
|
||||
'price_unit': 2000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_purchase_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
self.company_xmlid('demo_bill_cdnr_2'): {
|
||||
'ref': '000072',
|
||||
'move_type': 'in_refund',
|
||||
'partner_id': 'res_partner_registered_supplier_1',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_01',
|
||||
'quantity': 1,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_purchase_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
# Demo of B2CS (business to consumer small) Taxable supplies made to other unregistered Person and below INR 2.5 lakhs invoice value.
|
||||
self.company_xmlid('demo_invoice_b2cs'): {
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': 'res_partner_unregistered_customer_inter_state',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'consumer',
|
||||
'journal_id': sale_journal.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_16',
|
||||
'quantity': 1,
|
||||
'price_unit': 1500.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_sale_18')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_20',
|
||||
'quantity': 1,
|
||||
'price_unit': 2300.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_sale_18')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_22',
|
||||
'quantity': 1,
|
||||
'price_unit': 2600.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_sale_18')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_24',
|
||||
'quantity': 2,
|
||||
'price_unit': 1655.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_sale_5')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
# Demo of B2CL (business to consumer - Large) Taxable supplies made to other unregistered Person and invoice value is more than INR 2.5 lakhs.
|
||||
self.company_xmlid('demo_invoice_b2cl'): {
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': 'res_partner_unregistered_customer',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'consumer',
|
||||
'journal_id': sale_journal.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_01',
|
||||
'quantity': 3,
|
||||
'price_unit': 90000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_sale_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
# Demo of EXP(Export) supplies including supplies to SEZ/SEZ Developer or deemed exports.
|
||||
self.company_xmlid('demo_invoice_exp'): {
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': 'base.res_partner_3',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'overseas',
|
||||
'l10n_in_shipping_bill_number': '999704',
|
||||
'l10n_in_shipping_bill_date': time.strftime('%Y-%m-02'),
|
||||
'l10n_in_shipping_port_code_id': 'l10n_in.port_code_inixy1',
|
||||
'journal_id': sale_journal.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_4',
|
||||
'quantity': 30,
|
||||
'price_unit': 8000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('igst_sale_18_sez_exp')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
# Demo of exempt(Nil Rated, Exempted and Non GST supplies). Set Nill rated and Exempted tax in line.
|
||||
self.company_xmlid('demo_invoice_nill'): {
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': 'res_partner_registered_customer',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'journal_id': sale_journal.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_1',
|
||||
'quantity': 2,
|
||||
'price_unit': 25000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('exempt_sale')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_5',
|
||||
'quantity': 1,
|
||||
'price_unit': 400.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('nil_rated_sale')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
# Demo of cdnr(Credit/ Debit Note for registered person). Create credit note for demo b2b invoice.
|
||||
self.company_xmlid('demo_invoice_cdnr_1'): {
|
||||
'move_type': 'out_refund',
|
||||
'partner_id': 'res_partner_registered_customer',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'reversed_entry_id': 'demo_invoice_b2b_1',
|
||||
'journal_id': sale_journal.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_8',
|
||||
'quantity': 2,
|
||||
'price_unit': 40000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_sale_28')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_9',
|
||||
'quantity': 3,
|
||||
'price_unit': 400.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_sale_28'), _get_tax_by_id('cess_5_plus_1591_sale')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.product_product_10',
|
||||
'quantity': 4,
|
||||
'price_unit': 300.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_sale_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
self.company_xmlid('demo_invoice_cdnr_2'): {
|
||||
'move_type': 'out_refund',
|
||||
'partner_id': 'res_partner_registered_customer',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
'journal_id': sale_journal.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_01',
|
||||
'quantity': 1,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_sale_18')])],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_03',
|
||||
'quantity': 1,
|
||||
'price_unit': 2000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_sale_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
# Demo of cdnr(Credit/ Debit Note for unregistered person). Create credit note for demo b2cl invoice.
|
||||
self.company_xmlid('demo_invoice_cdnur'): {
|
||||
'move_type': 'out_refund',
|
||||
'partner_id': 'res_partner_unregistered_customer',
|
||||
'invoice_user_id': 'base.user_demo',
|
||||
'invoice_payment_term_id': 'account.account_payment_term_end_following_month',
|
||||
'invoice_date': datetime.now(),
|
||||
'l10n_in_gst_treatment': 'consumer',
|
||||
'reversed_entry_id': 'demo_invoice_b2cl',
|
||||
'journal_id': sale_journal.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': 'product.consu_delivery_01',
|
||||
'quantity': 3,
|
||||
'price_unit': 90000.0,
|
||||
'tax_ids': [Command.set([_get_tax_by_id('sgst_sale_18')])],
|
||||
}),
|
||||
]
|
||||
},
|
||||
}
|
||||
else:
|
||||
return super()._get_demo_data_move(company)
|
||||
|
||||
@api.model
|
||||
def _get_demo_data_attachment(self, company=False):
|
||||
if company.account_fiscal_country_id.code == "IN":
|
||||
return{
|
||||
'ir_attachment_in_invoice_1': {
|
||||
'type': 'binary',
|
||||
'name': 'in_invoice_demo_1.pdf',
|
||||
'res_model': 'account.move',
|
||||
'res_id': 'demo_invoice_to_extract',
|
||||
'raw': file_open(
|
||||
'l10n_in/static/demo/in_invoice_demo_1.pdf', 'rb'
|
||||
).read()
|
||||
},
|
||||
'ir_attachment_in_invoice_2': {
|
||||
'type': 'binary',
|
||||
'name': 'in_invoice_demo_2.pdf',
|
||||
'res_model': 'account.move',
|
||||
'res_id': 'demo_invoice_service',
|
||||
'raw': file_open(
|
||||
'l10n_in/static/demo/in_invoice_demo_2.pdf', 'rb'
|
||||
).read()
|
||||
}
|
||||
}
|
||||
else:
|
||||
return super()._get_demo_data_attachment(company)
|
||||
|
||||
|
||||
@api.model
|
||||
def _get_demo_data_mail_message(self, company=False):
|
||||
if company.account_fiscal_country_id.code == "IN":
|
||||
return {
|
||||
'mail_message_in_invoice_1': {
|
||||
'model': 'account.move',
|
||||
'res_id': 'demo_invoice_to_extract',
|
||||
'body': 'Vendor Bill attachment',
|
||||
'message_type': 'comment',
|
||||
'author_id': 'base.partner_demo',
|
||||
'attachment_ids': [Command.set([
|
||||
'ir_attachment_in_invoice_1',
|
||||
])]
|
||||
},
|
||||
'mail_message_in_invoice_2': {
|
||||
'model': 'account.move',
|
||||
'res_id': 'demo_invoice_service',
|
||||
'body': 'Vendor Bill attachment',
|
||||
'message_type': 'comment',
|
||||
'author_id': 'base.partner_demo',
|
||||
'attachment_ids': [Command.set([
|
||||
'ir_attachment_in_invoice_2',
|
||||
])]
|
||||
},
|
||||
}
|
||||
else:
|
||||
return super()._get_demo_data_mail_message(company)
|
||||
|
||||
def _post_load_demo_data(self, company=False):
|
||||
if company.account_fiscal_country_id.code == "IN":
|
||||
if company.state_id:
|
||||
invoices = (
|
||||
self.ref('demo_invoice_b2b_1')
|
||||
+ self.ref('demo_invoice_b2b_2')
|
||||
+ self.ref('demo_invoice_b2cs')
|
||||
+ self.ref('demo_invoice_b2cl')
|
||||
+ self.ref('demo_invoice_exp')
|
||||
+ self.ref('demo_invoice_nill')
|
||||
+ self.ref('demo_invoice_cdnr_1')
|
||||
+ self.ref('demo_invoice_cdnr_2')
|
||||
+ self.ref('demo_invoice_cdnur')
|
||||
+ self.ref('demo_bill_b2b_1')
|
||||
+ self.ref('demo_bill_b2b_2')
|
||||
+ self.ref('demo_bill_b2b_3')
|
||||
+ self.ref('demo_bill_imp')
|
||||
+ self.ref('demo_bill_cdnr_1')
|
||||
+ self.ref('demo_bill_cdnr_2')
|
||||
+ self.ref('demo_invoice_service')
|
||||
)
|
||||
for move in invoices:
|
||||
try:
|
||||
move.action_post()
|
||||
except (UserError, ValidationError):
|
||||
_logger.exception('Error while posting demo data')
|
||||
else:
|
||||
return super()._post_load_demo_data(company)
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo noupdate="1">
|
||||
|
||||
<record id="demo_invoice_b2b_intra_state" model="account.move">
|
||||
<field name="move_type">out_invoice</field>
|
||||
<field name="partner_id" ref="l10n_in.res_partner_registered_customer_intra_state"/>
|
||||
<field name="l10n_in_reseller_partner_id" ref="l10n_in.res_partner_reseller"/>
|
||||
<field name="invoice_user_id" ref="base.user_demo"/>
|
||||
<field name="invoice_payment_term_id" ref="account.account_payment_term_end_following_month"/>
|
||||
<field name="invoice_date" eval="time.strftime('%Y-%m')+'-01'"/>
|
||||
<field name="l10n_in_gst_treatment">regular</field>
|
||||
<field name="journal_id" model="account.journal"
|
||||
eval="obj().search([
|
||||
('type', '=', 'sale'),
|
||||
('company_id', '=', ref('l10n_in.demo_company_in'))], limit=1).id"/>
|
||||
<field name="invoice_line_ids" model="account.move.line" eval="[
|
||||
(0, 0, {
|
||||
'product_id': ref('product.product_product_8'),
|
||||
'quantity': 2,
|
||||
'price_unit': 40000.0,
|
||||
'tax_ids': [(6, 0, obj().tax_ids.search([
|
||||
('company_id', '=', ref('l10n_in.demo_company_in')),
|
||||
('type_tax_use', '=', 'sale'),
|
||||
('amount','=', 28),
|
||||
('tax_group_id', '=', ref('l10n_in.gst_group'))], limit=1).ids)]
|
||||
}),
|
||||
(0, 0, {
|
||||
'product_id': ref('product.product_product_9'),
|
||||
'quantity': 3,
|
||||
'price_unit': 400.0,
|
||||
'tax_ids': [(6, 0, obj().tax_ids.search([
|
||||
('company_id', '=', ref('l10n_in.demo_company_in')),
|
||||
('type_tax_use', '=', 'sale'),
|
||||
('amount','=', 18),
|
||||
('tax_group_id', '=', ref('l10n_in.gst_group'))], limit=1).ids)]
|
||||
}),
|
||||
(0, 0, {
|
||||
'product_id': ref('product.product_product_10'),
|
||||
'quantity': 4,
|
||||
'price_unit': 300.0,
|
||||
'tax_ids': [(6, 0, obj().tax_ids.search([
|
||||
('company_id', '=', ref('l10n_in.demo_company_in')),
|
||||
('type_tax_use', '=', 'sale'),
|
||||
'|',
|
||||
'&',
|
||||
('amount', '=', 18),
|
||||
('tax_group_id', '=', ref('l10n_in.gst_group')),
|
||||
'&',
|
||||
('tax_group_id', '=', ref('l10n_in.cess_group')),
|
||||
('children_tax_ids.amount','=', 5)
|
||||
], limit=2).ids)]
|
||||
}),
|
||||
]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -1,35 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="partner_demo_company_in" model="res.partner">
|
||||
<record id="base.partner_demo_company_in" model="res.partner" forcecreate="1">
|
||||
<field name="name">IN Company</field>
|
||||
<field name="vat">36AABCT1332L011</field>
|
||||
<field name="vat">24FANCY1234AAZA</field>
|
||||
<field name="l10n_in_tan">GANI12345A</field>
|
||||
<field name="street">Block no. 401</field>
|
||||
<field name="street2">Street 2</field>
|
||||
<field name="city">Hyderabad</field>
|
||||
<field name="city">Gandhinagar</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
<field name="state_id" ref="base.state_in_ts"/>
|
||||
<field name="zip">500001</field>
|
||||
<field name="state_id" ref="base.state_in_gj"/>
|
||||
<field name="zip">382002</field>
|
||||
<field name="phone">+91 81234 56789</field>
|
||||
<field name="email">info@company.inexample.com</field>
|
||||
<field name="website">www.inexample.com</field>
|
||||
<field name="is_company" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="demo_company_in" model="res.company">
|
||||
<record id="base.demo_company_in" model="res.company" forcecreate="1">
|
||||
<field name="name">IN Company</field>
|
||||
<field name="partner_id" ref="partner_demo_company_in"/>
|
||||
<field name="partner_id" ref="base.partner_demo_company_in"/>
|
||||
</record>
|
||||
|
||||
<function model="res.company" name="_onchange_country_id">
|
||||
<value eval="[ref('demo_company_in')]"/>
|
||||
<value eval="[ref('base.demo_company_in')]"/>
|
||||
</function>
|
||||
|
||||
<function model="res.users" name="write">
|
||||
<value eval="[ref('base.user_root'), ref('base.user_admin'), ref('base.user_demo')]"/>
|
||||
<value eval="{'company_ids': [(4, ref('l10n_in.demo_company_in'))]}"/>
|
||||
<value eval="{'company_ids': [(4, ref('base.demo_company_in'))]}"/>
|
||||
</function>
|
||||
|
||||
<function model="account.chart.template" name="try_loading">
|
||||
<value eval="[ref('l10n_in.indian_chart_template_standard')]"/>
|
||||
<value model="res.company" eval="obj().env.ref('l10n_in.demo_company_in')"/>
|
||||
<value eval="[]"/>
|
||||
<value>in</value>
|
||||
<value model="res.company" eval="obj().env.ref('base.demo_company_in')"/>
|
||||
<value name="install_demo" eval="True"/>
|
||||
</function>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -2,127 +2,96 @@
|
|||
<odoo noupdate="1">
|
||||
<record id="product.product_product_1" model="product.product">
|
||||
<field name="l10n_in_hsn_code">998391</field>
|
||||
<field name="l10n_in_hsn_description">Specialty Design Services Including Interior Design, Fashion Design, Industrial Design And Other Specialty Design Services</field>
|
||||
</record>
|
||||
<record id="product.product_product_2" model="product.product">
|
||||
<field name="l10n_in_hsn_code">998391</field>
|
||||
<field name="l10n_in_hsn_description">Specialty Design Services Including Interior Design, Fashion Design, Industrial Design And Other Specialty Design Services</field>
|
||||
</record>
|
||||
<record id="product.product_product_3" 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="product.product_product_4" 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="product.product_product_4b" 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="product.product_product_4c" 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="product.product_product_5" 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="product.product_product_6" 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="product.product_product_7" model="product.product">
|
||||
<field name="l10n_in_hsn_code">48196000</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>
|
||||
</record>
|
||||
<record id="product.product_product_8" 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="product.product_product_9" model="product.product">
|
||||
<field name="l10n_in_hsn_code">7323</field>
|
||||
<field name="l10n_in_hsn_description">Table, kitchen or other household articles and parts thereof, of iron or steel; iron or steel wool; pot scourers and scouring or polishing pads, gloves and the like, of iron or steel.</field>
|
||||
</record>
|
||||
<record id="product.product_product_10" model="product.product">
|
||||
<field name="l10n_in_hsn_code">84185000</field>
|
||||
<field name="l10n_in_hsn_description">Other furniture (chests, cabinets, display counters, show-cases and the like) for storage and display, incorporating refrigerating or freezing equipment</field>
|
||||
</record>
|
||||
<record id="product.product_product_11" model="product.product">
|
||||
<field name="l10n_in_hsn_code">94018000</field>
|
||||
<field name="l10n_in_hsn_description">Seats (other than those of heading 9402), whether or not convertible into beds, and parts thereof</field>
|
||||
</record>
|
||||
<record id="product.product_product_11b" model="product.product">
|
||||
<field name="l10n_in_hsn_code">94018000</field>
|
||||
<field name="l10n_in_hsn_description">Seats (other than those of heading 9402), whether or not convertible into beds, and parts thereof</field>
|
||||
</record>
|
||||
<record id="product.product_product_12" model="product.product">
|
||||
<field name="l10n_in_hsn_code">94018000</field>
|
||||
<field name="l10n_in_hsn_description">Seats (other than those of heading 9402), whether or not convertible into beds, and parts thereof</field>
|
||||
</record>
|
||||
<record id="product.product_product_13" 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="product.product_product_16" model="product.product">
|
||||
<field name="l10n_in_hsn_code">94031090</field>
|
||||
<field name="l10n_in_hsn_description">Metal furniture of a kind used in offices</field>
|
||||
</record>
|
||||
<record id="product.product_product_20" model="product.product">
|
||||
<field name="l10n_in_hsn_code">37011090</field>
|
||||
<field name="l10n_in_hsn_description">Photographic plates and film in the flat, sensitised, unexposed, of any material other than paper, paperboard or textiles; instant print film in the flat, sensitised, unexposed, whether or not in packs.</field>
|
||||
</record>
|
||||
<record id="product.product_product_22" 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="product.product_product_24" model="product.product">
|
||||
<field name="l10n_in_hsn_code">94031090</field>
|
||||
<field name="l10n_in_hsn_description">Metal furniture of a kind used in offices</field>
|
||||
</record>
|
||||
<record id="product.product_product_25" model="product.product">
|
||||
<field name="l10n_in_hsn_code">94031090</field>
|
||||
<field name="l10n_in_hsn_description">Metal furniture of a kind used in offices</field>
|
||||
</record>
|
||||
<record id="product.product_product_27" model="product.product">
|
||||
<field name="l10n_in_hsn_code">94031090</field>
|
||||
<field name="l10n_in_hsn_description">Metal furniture of a kind used in offices</field>
|
||||
</record>
|
||||
|
||||
<!-- Expensable products -->
|
||||
<record id="product.expense_product" model="product.product">
|
||||
<field name="l10n_in_hsn_code">9963.31</field>
|
||||
<field name="l10n_in_hsn_description">Services provided by Restaurants, Cafes and similar eating facilities including takeaway services, Room services and door delivery of food.
|
||||
</field>
|
||||
<field name="l10n_in_hsn_code">996331</field>
|
||||
</record>
|
||||
<record id="product.expense_hotel" model="product.product">
|
||||
<field name="l10n_in_hsn_code">9963.32</field>
|
||||
<field name="l10n_in_hsn_description">Services provided by Hotels, INN, Guest House, Club etc including Room services, takeaway services and door delivery of food.</field>
|
||||
<field name="l10n_in_hsn_code">996332</field>
|
||||
</record>
|
||||
|
||||
<!-- Physical Products -->
|
||||
<record id="product.product_delivery_01" model="product.product">
|
||||
<field name="l10n_in_hsn_code">94018000</field>
|
||||
<field name="l10n_in_hsn_description">Seats (other than those of heading 9402), whether or not convertible into beds, and parts thereof</field>
|
||||
</record>
|
||||
<record id="product.product_delivery_02" model="product.product">
|
||||
<field name="l10n_in_hsn_code">94051090</field>
|
||||
<field name="l10n_in_hsn_description">Lamps and lighting fittings including searchlights and spotlights and parts thereof, not elsewhere specified or included; illuminated signs, illuminated name-plates and the like, having a permanently fixed light source, and parts thereof not elsewhere specified or included</field>
|
||||
</record>
|
||||
<record id="product.product_order_01" model="product.product">
|
||||
<field name="l10n_in_hsn_code">4911.99.10</field>
|
||||
<field name="l10n_in_hsn_description">Hard copy (printed) of computer software</field>
|
||||
<field name="l10n_in_hsn_code">49119910</field>
|
||||
</record>
|
||||
<record id="product.consu_delivery_01" model="product.product">
|
||||
<field name="l10n_in_hsn_code">9401.61.00</field>
|
||||
<field name="l10n_in_hsn_description">Seats (other than those of heading 9402), whether or not convertible into beds, and parts thereof</field>
|
||||
<field name="l10n_in_hsn_code">94016100</field>
|
||||
</record>
|
||||
<record id="product.consu_delivery_02" model="product.product">
|
||||
<field name="l10n_in_hsn_code">9403.89.00</field>
|
||||
<field name="l10n_in_hsn_description">Other Furniture</field>
|
||||
<field name="l10n_in_hsn_code">94038900</field>
|
||||
</record>
|
||||
<record id="product.consu_delivery_03" model="product.product">
|
||||
<field name="l10n_in_hsn_code">9403</field>
|
||||
<field name="l10n_in_hsn_description">Other furniture and parts thereof.</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo noupdate="1">
|
||||
<record id="res_partner_category_registered" model="res.partner.category">
|
||||
<field name="name">Registered</field>
|
||||
<field name="color" eval="2"/>
|
||||
</record>
|
||||
<record id="res_partner_category_unregistered" model="res.partner.category">
|
||||
<field name="name">Unregistered</field>
|
||||
<field name="color" eval="3"/>
|
||||
</record>
|
||||
<record id="res_partner_category_reseller" model="res.partner.category">
|
||||
<field name="name">Reseller</field>
|
||||
<field name="color" eval="12"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_registered_customer_intra_state" model="res.partner">
|
||||
<field name="name">Registered Customer Intra State</field>
|
||||
<field eval="[(6, 0, [ref('l10n_in.res_partner_category_registered')])]" name="category_id"/>
|
||||
<field name="is_company">1</field>
|
||||
<field name="l10n_in_gst_treatment">regular</field>
|
||||
<field name="street">floor-1, Maddikunta-Ankanpally Village</field>
|
||||
<field name="street2">Post box No 2, NH-65</field>
|
||||
<field name="city">Sangareddy</field>
|
||||
<field name="zip">500002</field>
|
||||
<field name="state_id" ref="base.state_in_ts"/>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
<field name="vat">36AAACM4154G1ZO</field>
|
||||
</record>
|
||||
|
||||
<!-- reseller partner -->
|
||||
<record id="res_partner_reseller" model="res.partner">
|
||||
<field name="name">Reseller(E-Commerce)</field>
|
||||
<field eval="[(6, 0, [ref('l10n_in.res_partner_category_reseller'),
|
||||
ref('l10n_in.res_partner_category_registered')])]" name="category_id"/>
|
||||
<field name="street">4/001 Ground Floor, 16th Main Rd,</field>
|
||||
<field name="l10n_in_gst_treatment">regular</field>
|
||||
<field name="city">Bengaluru</field>
|
||||
<field name="zip">560001</field>
|
||||
<field name="state_id" ref="base.state_in_ka"/>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
<field name="vat">29AJIPA1572E1ZR</field>
|
||||
</record>
|
||||
</odoo>
|
||||
2680
odoo-bringout-oca-ocb-l10n_in/l10n_in/i18n/hi.po
Normal file
2680
odoo-bringout-oca-ocb-l10n_in/l10n_in/i18n/hi.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import odoo
|
||||
from odoo.modules.registry import Registry
|
||||
|
||||
def migrate(cr, version):
|
||||
registry = odoo.registry(cr.dbname)
|
||||
registry = Registry(cr.dbname)
|
||||
from odoo.addons.account.models.chart_template import migrate_set_tags_and_taxes_updatable
|
||||
migrate_set_tags_and_taxes_updatable(cr, registry, 'l10n_in')
|
||||
|
|
|
|||
|
|
@ -1,13 +1,19 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account
|
||||
from . import template_in
|
||||
from . import account_invoice
|
||||
from . import chart_template
|
||||
from . import account_journal
|
||||
from . import account_move_line
|
||||
from . import account_payment
|
||||
from . import account_tax
|
||||
from . import company
|
||||
from . import iap_account
|
||||
from . import product_template
|
||||
from . import port_code
|
||||
from . import res_config_settings
|
||||
from . import res_country_state
|
||||
from . import res_partner
|
||||
from . import uom_uom
|
||||
from . import mail_message
|
||||
from . import account_account
|
||||
from . import l10n_in_section_alert
|
||||
from . import l10n_in_pan_entity
|
||||
from . import l10n_in_report_handler
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo import tools
|
||||
|
||||
|
||||
class AccountJournal(models.Model):
|
||||
_inherit = "account.journal"
|
||||
|
||||
# Use for filter import and export type.
|
||||
l10n_in_gstin_partner_id = fields.Many2one('res.partner', string="GSTIN Unit", ondelete="restrict", help="GSTIN related to this journal. If empty then consider as company GSTIN.")
|
||||
|
||||
def name_get(self):
|
||||
"""
|
||||
Add GSTIN number in name as suffix so user can easily find the right journal.
|
||||
Used super to ensure nothing is missed.
|
||||
"""
|
||||
result = super().name_get()
|
||||
result_dict = dict(result)
|
||||
indian_journals = self.filtered(lambda j: j.company_id.account_fiscal_country_id.code == 'IN' and
|
||||
j.l10n_in_gstin_partner_id and j.l10n_in_gstin_partner_id.vat)
|
||||
for journal in indian_journals:
|
||||
name = result_dict[journal.id]
|
||||
name += "- %s" % (journal.l10n_in_gstin_partner_id.vat)
|
||||
result_dict[journal.id] = name
|
||||
return list(result_dict.items())
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
def init(self):
|
||||
tools.create_index(self._cr, 'account_move_line_move_product_index', self._table, ['move_id', 'product_id'])
|
||||
|
||||
@api.depends('move_id.line_ids', 'move_id.line_ids.tax_line_id', 'move_id.line_ids.debit', 'move_id.line_ids.credit')
|
||||
def _compute_tax_base_amount(self):
|
||||
aml = self.filtered(lambda l: l.company_id.account_fiscal_country_id.code == 'IN' and l.tax_line_id and l.product_id)
|
||||
for move_line in aml:
|
||||
base_lines = move_line.move_id.line_ids.filtered(lambda line: move_line.tax_line_id in line.tax_ids and move_line.product_id == line.product_id)
|
||||
move_line.tax_base_amount = abs(sum(base_lines.mapped('balance')))
|
||||
remaining_aml = self - aml
|
||||
if remaining_aml:
|
||||
return super(AccountMoveLine, remaining_aml)._compute_tax_base_amount()
|
||||
|
||||
|
||||
class AccountTax(models.Model):
|
||||
_inherit = 'account.tax'
|
||||
|
||||
l10n_in_reverse_charge = fields.Boolean("Reverse charge", help="Tick this if this tax is reverse charge. Only for Indian accounting")
|
||||
|
||||
@api.model
|
||||
def _get_generation_dict_from_base_line(self, line_vals, tax_vals, force_caba_exigibility=False):
|
||||
# EXTENDS account
|
||||
# Group taxes also by product.
|
||||
res = super()._get_generation_dict_from_base_line(line_vals, tax_vals, force_caba_exigibility=force_caba_exigibility)
|
||||
record = line_vals['record']
|
||||
if isinstance(record, models.Model)\
|
||||
and record._name == 'account.move.line'\
|
||||
and record.company_id.account_fiscal_country_id.code == 'IN':
|
||||
res['product_id'] = record.product_id.id
|
||||
res['product_uom_id'] = record.product_uom_id.id
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _get_generation_dict_from_tax_line(self, line_vals):
|
||||
# EXTENDS account
|
||||
# Group taxes also by product.
|
||||
res = super()._get_generation_dict_from_tax_line(line_vals)
|
||||
record = line_vals['record']
|
||||
if isinstance(record, models.Model)\
|
||||
and record._name == 'account.move.line'\
|
||||
and record.company_id.account_fiscal_country_id.code == 'IN':
|
||||
res['product_id'] = record.product_id.id
|
||||
res['product_uom_id'] = record.product_uom_id.id
|
||||
return res
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class AccountAccount(models.Model):
|
||||
_inherit = 'account.account'
|
||||
|
||||
l10n_in_tds_tcs_section_id = fields.Many2one('l10n_in.section.alert', string="TCS/TDS Section")
|
||||
l10n_in_tds_feature_enabled = fields.Boolean(compute='_compute_tds_tcs_features', store=True)
|
||||
l10n_in_tcs_feature_enabled = fields.Boolean(compute='_compute_tds_tcs_features', store=True)
|
||||
|
||||
@api.depends('company_ids.l10n_in_tds_feature', 'company_ids.l10n_in_tcs_feature')
|
||||
def _compute_tds_tcs_features(self):
|
||||
for record in self:
|
||||
record.l10n_in_tds_feature_enabled = any(company.l10n_in_tds_feature for company in record.company_ids)
|
||||
record.l10n_in_tcs_feature_enabled = any(company.l10n_in_tcs_feature for company in record.company_ids)
|
||||
|
|
@ -1,19 +1,34 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base64
|
||||
import logging
|
||||
import json
|
||||
import re
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from contextlib import contextmanager
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import Command, _, api, fields, models
|
||||
from odoo.exceptions import ValidationError, RedirectWarning, UserError
|
||||
from odoo.tools.float_utils import json_float_round
|
||||
from odoo.tools.image import image_data_uri
|
||||
from odoo.tools import float_compare, SQL
|
||||
from odoo.tools.date_utils import get_month
|
||||
from odoo.addons.l10n_in.models.iap_account import IAP_SERVICE_NAME
|
||||
|
||||
EDI_CANCEL_REASON = {
|
||||
# Same for both e-way bill and IRN
|
||||
'1': "Duplicate",
|
||||
'2': "Data Entry Mistake",
|
||||
'3': "Order Cancelled",
|
||||
'4': "Others",
|
||||
}
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
amount_total_words = fields.Char("Total (In Words)", compute="_compute_amount_total_words")
|
||||
l10n_in_gst_treatment = fields.Selection([
|
||||
l10n_in_gst_treatment = fields.Selection(
|
||||
selection=[
|
||||
('regular', 'Registered Business - Regular'),
|
||||
('composition', 'Registered Business - Composition'),
|
||||
('unregistered', 'Unregistered Business'),
|
||||
|
|
@ -22,57 +37,517 @@ class AccountMove(models.Model):
|
|||
('special_economic_zone', 'Special Economic Zone'),
|
||||
('deemed_export', 'Deemed Export'),
|
||||
('uin_holders', 'UIN Holders'),
|
||||
], string="GST Treatment", compute="_compute_l10n_in_gst_treatment", store=True, readonly=False, copy=True)
|
||||
l10n_in_state_id = fields.Many2one('res.country.state', string="Place of supply", compute="_compute_l10n_in_state_id", store=True, readonly=False)
|
||||
],
|
||||
string="GST Treatment",
|
||||
compute="_compute_l10n_in_gst_treatment",
|
||||
store=True,
|
||||
readonly=False,
|
||||
copy=True,
|
||||
precompute=True
|
||||
)
|
||||
l10n_in_state_id = fields.Many2one(
|
||||
comodel_name='res.country.state',
|
||||
string="Place of supply",
|
||||
compute="_compute_l10n_in_state_id",
|
||||
store=True,
|
||||
copy=True,
|
||||
readonly=False,
|
||||
precompute=True
|
||||
)
|
||||
l10n_in_gstin = fields.Char(string="GSTIN")
|
||||
# For Export invoice this data is need in GSTR report
|
||||
l10n_in_shipping_bill_number = fields.Char('Shipping bill number', readonly=True, states={'draft': [('readonly', False)]})
|
||||
l10n_in_shipping_bill_date = fields.Date('Shipping bill date', readonly=True, states={'draft': [('readonly', False)]})
|
||||
l10n_in_shipping_port_code_id = fields.Many2one('l10n_in.port.code', 'Port code', readonly=True, states={'draft': [('readonly', False)]})
|
||||
l10n_in_reseller_partner_id = fields.Many2one('res.partner', 'Reseller', domain=[('vat', '!=', False)], help="Only Registered Reseller", readonly=True, states={'draft': [('readonly', False)]})
|
||||
l10n_in_shipping_bill_number = fields.Char('Shipping bill number')
|
||||
l10n_in_shipping_bill_date = fields.Date('Shipping bill date')
|
||||
l10n_in_shipping_port_code_id = fields.Many2one('l10n_in.port.code', 'Port code')
|
||||
l10n_in_reseller_partner_id = fields.Many2one(
|
||||
comodel_name='res.partner',
|
||||
string="Reseller",
|
||||
domain=[('vat', '!=', False)],
|
||||
help="Only Registered Reseller"
|
||||
)
|
||||
l10n_in_journal_type = fields.Selection(string="Journal Type", related='journal_id.type')
|
||||
l10n_in_warning = fields.Json(compute="_compute_l10n_in_warning")
|
||||
l10n_in_is_gst_registered_enabled = fields.Boolean(related='company_id.l10n_in_is_gst_registered')
|
||||
l10n_in_tds_deduction = fields.Selection(related='commercial_partner_id.l10n_in_pan_entity_id.tds_deduction', string="TDS Deduction")
|
||||
|
||||
@api.depends('amount_total')
|
||||
def _compute_amount_total_words(self):
|
||||
for invoice in self:
|
||||
invoice.amount_total_words = invoice.currency_id.amount_to_text(invoice.amount_total)
|
||||
# withholding related fields
|
||||
l10n_in_is_withholding = fields.Boolean(
|
||||
string="Is Indian TDS Entry",
|
||||
copy=False,
|
||||
help="Technical field to identify Indian withholding entry"
|
||||
)
|
||||
l10n_in_withholding_ref_move_id = fields.Many2one(
|
||||
comodel_name='account.move',
|
||||
string="Indian TDS Ref Move",
|
||||
readonly=True,
|
||||
index='btree_not_null',
|
||||
copy=False,
|
||||
help="Reference move for withholding entry",
|
||||
)
|
||||
l10n_in_withholding_ref_payment_id = fields.Many2one(
|
||||
comodel_name='account.payment',
|
||||
string="Indian TDS Ref Payment",
|
||||
index='btree_not_null',
|
||||
readonly=True,
|
||||
copy=False,
|
||||
help="Reference Payment for withholding entry",
|
||||
)
|
||||
l10n_in_withhold_move_ids = fields.One2many(
|
||||
'account.move', 'l10n_in_withholding_ref_move_id',
|
||||
string="Indian TDS Entries"
|
||||
)
|
||||
l10n_in_withholding_line_ids = fields.One2many(
|
||||
'account.move.line', 'move_id',
|
||||
string="Indian TDS Lines",
|
||||
compute='_compute_l10n_in_withholding_line_ids',
|
||||
)
|
||||
l10n_in_total_withholding_amount = fields.Monetary(
|
||||
string="Total Indian TDS Amount",
|
||||
compute='_compute_l10n_in_total_withholding_amount',
|
||||
help="Total withholding amount for the move",
|
||||
)
|
||||
l10n_in_display_higher_tcs_button = fields.Boolean(string="Display higher TCS button", compute="_compute_l10n_in_display_higher_tcs_button")
|
||||
l10n_in_tds_feature_enabled = fields.Boolean(related='company_id.l10n_in_tds_feature')
|
||||
l10n_in_tcs_feature_enabled = fields.Boolean(related='company_id.l10n_in_tcs_feature')
|
||||
|
||||
# gstin_status related field
|
||||
l10n_in_partner_gstin_status = fields.Boolean(
|
||||
string="GST Status",
|
||||
compute="_compute_l10n_in_partner_gstin_status_and_date",
|
||||
)
|
||||
l10n_in_show_gstin_status = fields.Boolean(compute="_compute_l10n_in_show_gstin_status")
|
||||
l10n_in_gstin_verified_date = fields.Date(compute="_compute_l10n_in_partner_gstin_status_and_date")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# COMPUTE METHODS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_l10n_in_gst_treatment(self):
|
||||
indian_invoice = self.filtered(lambda m: m.country_code == 'IN')
|
||||
for record in indian_invoice:
|
||||
gst_treatment = record.partner_id.l10n_in_gst_treatment
|
||||
if not gst_treatment:
|
||||
gst_treatment = 'unregistered'
|
||||
if record.partner_id.country_id.code == 'IN' and record.partner_id.vat:
|
||||
gst_treatment = 'regular'
|
||||
elif record.partner_id.country_id and record.partner_id.country_id.code != 'IN':
|
||||
gst_treatment = 'overseas'
|
||||
record.l10n_in_gst_treatment = gst_treatment
|
||||
(self - indian_invoice).l10n_in_gst_treatment = False
|
||||
for invoice in self.filtered(lambda m: m.country_code == 'IN' and m.state == 'draft'):
|
||||
partner = invoice.partner_id
|
||||
invoice.l10n_in_gst_treatment = (
|
||||
partner.l10n_in_gst_treatment
|
||||
or (
|
||||
'overseas' if partner.country_id and partner.country_id.code != 'IN'
|
||||
else partner.check_vat_in(partner.vat) and 'regular' or 'consumer'
|
||||
)
|
||||
)
|
||||
|
||||
@api.depends('partner_id', 'company_id')
|
||||
@api.depends('partner_id', 'partner_shipping_id', 'company_id')
|
||||
def _compute_l10n_in_state_id(self):
|
||||
foreign_state = self.env.ref('l10n_in.state_in_oc', raise_if_not_found=False)
|
||||
for move in self:
|
||||
if move.country_code == 'IN' and move.journal_id.type == 'sale':
|
||||
country_code = move.partner_id.country_id.code
|
||||
if country_code == 'IN':
|
||||
move.l10n_in_state_id = move.partner_id.state_id
|
||||
elif country_code:
|
||||
move.l10n_in_state_id = self.env.ref('l10n_in.state_in_oc', raise_if_not_found=False)
|
||||
else:
|
||||
move.l10n_in_state_id = move.company_id.state_id
|
||||
if move.country_code == 'IN' and move.is_sale_document(include_receipts=True):
|
||||
partner = (
|
||||
move.partner_id.commercial_partner_id == move.partner_shipping_id.commercial_partner_id
|
||||
and move.partner_shipping_id
|
||||
or move.partner_id
|
||||
)
|
||||
if partner.country_id and partner.country_id.code != 'IN':
|
||||
move.l10n_in_state_id = foreign_state
|
||||
continue
|
||||
partner_state = partner.state_id or move.partner_id.commercial_partner_id.state_id or move.company_id.state_id
|
||||
country_code = partner_state.country_id.code or move.country_code
|
||||
move.l10n_in_state_id = partner_state if country_code == 'IN' else foreign_state
|
||||
elif move.country_code == 'IN' and move.journal_id.type == 'purchase':
|
||||
move.l10n_in_state_id = move.company_id.state_id
|
||||
else:
|
||||
move.l10n_in_state_id = False
|
||||
|
||||
@api.depends('l10n_in_state_id', 'l10n_in_gst_treatment')
|
||||
def _compute_fiscal_position_id(self):
|
||||
|
||||
foreign_state = self.env['res.country.state'].search([('code', '!=', 'IN')], limit=1)
|
||||
|
||||
def _get_fiscal_state(move):
|
||||
"""
|
||||
Maps each move to its corresponding fiscal state based on its type,
|
||||
fiscal conditions, and the state of the associated partner or company.
|
||||
"""
|
||||
|
||||
if (
|
||||
move.country_code != 'IN'
|
||||
or not move.is_invoice(include_receipts=True)
|
||||
# Partner's FP takes precedence through super
|
||||
or move.partner_shipping_id.property_account_position_id
|
||||
or move.partner_id.property_account_position_id
|
||||
):
|
||||
return False
|
||||
elif move.l10n_in_gst_treatment == 'special_economic_zone':
|
||||
# Special Economic Zone
|
||||
return self.env.ref('l10n_in.state_in_oc')
|
||||
elif move.is_sale_document(include_receipts=True):
|
||||
# In Sales Documents: Compare place of supply with company state
|
||||
return move.l10n_in_state_id if move.l10n_in_state_id.l10n_in_tin != '96' else foreign_state
|
||||
elif move.is_purchase_document(include_receipts=True) and move.partner_id.country_id.code == 'IN':
|
||||
# In Purchases Documents: Compare place of supply with vendor state
|
||||
pos_state_id = move.l10n_in_state_id
|
||||
if pos_state_id.l10n_in_tin == '96':
|
||||
return pos_state_id
|
||||
elif pos_state_id == move.partner_id.state_id:
|
||||
# Intra-State: Group by state matching the company's state.
|
||||
return move.company_id.state_id
|
||||
elif pos_state_id != move.partner_id.state_id:
|
||||
# Inter-State: Group by state that doesn't match the company's state.
|
||||
return (
|
||||
pos_state_id == move.company_id.state_id
|
||||
and move.partner_id.state_id
|
||||
or pos_state_id
|
||||
)
|
||||
return False
|
||||
|
||||
FiscalPosition = self.env['account.fiscal.position']
|
||||
for state_id, moves in self.grouped(_get_fiscal_state).items():
|
||||
if state_id:
|
||||
virtual_partner = self.env['res.partner'].new({
|
||||
'state_id': state_id.id,
|
||||
'country_id': state_id.country_id.id,
|
||||
})
|
||||
# Group moves by company to avoid multi-company conflicts
|
||||
for company_id, company_moves in moves.grouped('company_id').items():
|
||||
company_moves.fiscal_position_id = FiscalPosition.with_company(
|
||||
company_id
|
||||
)._get_fiscal_position(virtual_partner)
|
||||
else:
|
||||
super(AccountMove, moves)._compute_fiscal_position_id()
|
||||
|
||||
@api.onchange('name')
|
||||
def _onchange_name_warning(self):
|
||||
if (
|
||||
self.country_code == 'IN'
|
||||
and self.company_id.l10n_in_is_gst_registered
|
||||
and self.journal_id.type == 'sale'
|
||||
and self.name
|
||||
and (len(self.name) > 16 or not re.match(r'^[a-zA-Z0-9-\/]+$', self.name))
|
||||
):
|
||||
return {'warning': {
|
||||
'title' : _("Invalid sequence as per GST rule 46(b)"),
|
||||
'message': _(
|
||||
"The invoice number should not exceed 16 characters\n"
|
||||
"and must only contain '-' (hyphen) and '/' (slash) as special characters"
|
||||
)
|
||||
}}
|
||||
return super()._onchange_name_warning()
|
||||
|
||||
@api.depends(
|
||||
'invoice_line_ids.l10n_in_hsn_code',
|
||||
'company_id.l10n_in_hsn_code_digit',
|
||||
'invoice_line_ids.tax_ids',
|
||||
'commercial_partner_id.l10n_in_pan_entity_id',
|
||||
'invoice_line_ids.price_total'
|
||||
)
|
||||
def _compute_l10n_in_warning(self):
|
||||
indian_invoice = self.filtered(lambda m: m.country_code == 'IN' and m.move_type != 'entry')
|
||||
line_filter_func = lambda line: line.display_type == 'product' and line.tax_ids and line._origin
|
||||
_xmlid_to_res_id = self.env['ir.model.data']._xmlid_to_res_id
|
||||
for move in indian_invoice:
|
||||
warnings = {}
|
||||
company = move.company_id
|
||||
action_name = _("Journal Item(s)")
|
||||
action_text = _("View Journal Item(s)")
|
||||
if company.l10n_in_tcs_feature or company.l10n_in_tds_feature:
|
||||
invalid_tax_lines = move._get_l10n_in_invalid_tax_lines()
|
||||
if company.l10n_in_tcs_feature and invalid_tax_lines:
|
||||
warnings['lower_tcs_tax'] = {
|
||||
'message': _("As the Partner's PAN missing/invalid apply TCS at the higher rate."),
|
||||
'actions': invalid_tax_lines.with_context(tax_validation=True)._get_records_action(
|
||||
name=action_name,
|
||||
views=[(_xmlid_to_res_id("l10n_in.view_move_line_tree_hsn_l10n_in"), "list")],
|
||||
domain=[('id', 'in', invalid_tax_lines.ids)],
|
||||
),
|
||||
'action_text': action_text,
|
||||
}
|
||||
|
||||
if applicable_sections := move._get_l10n_in_tds_tcs_applicable_sections():
|
||||
tds_tcs_applicable_lines = (
|
||||
move.move_type == 'out_invoice'
|
||||
and move._get_tcs_applicable_lines(move.invoice_line_ids)
|
||||
or move.invoice_line_ids
|
||||
)
|
||||
warnings['tds_tcs_threshold_alert'] = {
|
||||
'message': applicable_sections._get_warning_message(),
|
||||
'action': tds_tcs_applicable_lines.with_context(
|
||||
default_tax_type_use=True,
|
||||
move_type=move.move_type == 'in_invoice'
|
||||
)._get_records_action(
|
||||
name=action_name,
|
||||
domain=[('id', 'in', tds_tcs_applicable_lines.ids)],
|
||||
views=[(_xmlid_to_res_id("l10n_in.view_move_line_list_l10n_in_withholding"), "list")]
|
||||
),
|
||||
'action_text': action_text,
|
||||
}
|
||||
|
||||
if (
|
||||
company.l10n_in_is_gst_registered
|
||||
and company.l10n_in_hsn_code_digit
|
||||
and (filtered_lines := move.invoice_line_ids.filtered(line_filter_func))
|
||||
):
|
||||
lines = self.env['account.move.line']
|
||||
for line in filtered_lines:
|
||||
hsn_code = line.l10n_in_hsn_code
|
||||
if (
|
||||
not hsn_code
|
||||
or (
|
||||
not re.match(r'^\d{4}$|^\d{6}$|^\d{8}$', hsn_code)
|
||||
or len(hsn_code) < int(company.l10n_in_hsn_code_digit)
|
||||
)
|
||||
):
|
||||
lines |= line._origin
|
||||
|
||||
if lines:
|
||||
digit_suffixes = {
|
||||
'4': _("4 digits, 6 digits or 8 digits"),
|
||||
'6': _("6 digits or 8 digits"),
|
||||
'8': _("8 digits")
|
||||
}
|
||||
msg = _(
|
||||
"Ensure that the HSN/SAC Code consists either %s in invoice lines",
|
||||
digit_suffixes.get(company.l10n_in_hsn_code_digit, _("Invalid HSN/SAC Code digit"))
|
||||
)
|
||||
warnings['invalid_hsn_code_length'] = {
|
||||
'message': msg,
|
||||
'action': lines._get_records_action(
|
||||
name=action_name,
|
||||
views=[(_xmlid_to_res_id("l10n_in.view_move_line_tree_hsn_l10n_in"), "list")],
|
||||
domain=[('id', 'in', lines.ids)]
|
||||
),
|
||||
'action_text': action_text,
|
||||
}
|
||||
|
||||
move.l10n_in_warning = warnings
|
||||
(self - indian_invoice).l10n_in_warning = {}
|
||||
|
||||
@api.depends('partner_id', 'state', 'payment_state', 'l10n_in_gst_treatment')
|
||||
def _compute_l10n_in_show_gstin_status(self):
|
||||
indian_moves = self.filtered(
|
||||
lambda m: m.country_code == 'IN' and m.company_id.l10n_in_gstin_status_feature
|
||||
)
|
||||
(self - indian_moves).l10n_in_show_gstin_status = False
|
||||
for move in indian_moves:
|
||||
move.l10n_in_show_gstin_status = (
|
||||
move.partner_id
|
||||
and move.state == 'posted'
|
||||
and move.move_type != 'entry'
|
||||
and move.payment_state not in ['paid', 'reversed']
|
||||
and move.l10n_in_gst_treatment in [
|
||||
'regular',
|
||||
'composition',
|
||||
'special_economic_zone',
|
||||
'deemed_export',
|
||||
'uin_holders'
|
||||
]
|
||||
)
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_l10n_in_partner_gstin_status_and_date(self):
|
||||
for move in self:
|
||||
if (
|
||||
move.country_code == 'IN'
|
||||
and move.company_id.l10n_in_gstin_status_feature
|
||||
and move.payment_state not in ['paid', 'reversed']
|
||||
and move.state != 'cancel'
|
||||
):
|
||||
move.l10n_in_partner_gstin_status = move.partner_id.l10n_in_gstin_verified_status
|
||||
move.l10n_in_gstin_verified_date = move.partner_id.l10n_in_gstin_verified_date
|
||||
else:
|
||||
move.l10n_in_partner_gstin_status = False
|
||||
move.l10n_in_gstin_verified_date = False
|
||||
|
||||
@api.depends('line_ids', 'l10n_in_is_withholding')
|
||||
def _compute_l10n_in_withholding_line_ids(self):
|
||||
# Compute the withholding lines for the move
|
||||
for move in self:
|
||||
if move.l10n_in_is_withholding:
|
||||
move.l10n_in_withholding_line_ids = move.line_ids.filtered('tax_ids')
|
||||
else:
|
||||
move.l10n_in_withholding_line_ids = False
|
||||
|
||||
def _compute_l10n_in_total_withholding_amount(self):
|
||||
for move in self:
|
||||
if self.env.company.l10n_in_tds_feature:
|
||||
move.l10n_in_total_withholding_amount = sum(
|
||||
move.l10n_in_withhold_move_ids.filtered(
|
||||
lambda m: m.state == 'posted'
|
||||
).l10n_in_withholding_line_ids.mapped('l10n_in_withhold_tax_amount')
|
||||
)
|
||||
else:
|
||||
move.l10n_in_total_withholding_amount = 0.0
|
||||
|
||||
@api.depends('l10n_in_warning')
|
||||
def _compute_l10n_in_display_higher_tcs_button(self):
|
||||
for move in self:
|
||||
if move.company_id.l10n_in_tcs_feature:
|
||||
move.l10n_in_display_higher_tcs_button = (
|
||||
move.l10n_in_warning
|
||||
and move.l10n_in_warning.get('lower_tcs_tax')
|
||||
)
|
||||
else:
|
||||
move.l10n_in_display_higher_tcs_button = False
|
||||
|
||||
def action_l10n_in_withholding_entries(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': "TDS Entries",
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'account.move',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('id', 'in', self.l10n_in_withhold_move_ids.ids)],
|
||||
}
|
||||
|
||||
def action_l10n_in_apply_higher_tax(self):
|
||||
self.ensure_one()
|
||||
invalid_lines = self._get_l10n_in_invalid_tax_lines()
|
||||
for line in invalid_lines:
|
||||
updated_tax_ids = []
|
||||
for tax in line.tax_ids:
|
||||
if tax.l10n_in_tax_type == 'tcs':
|
||||
max_tax = max(
|
||||
tax.l10n_in_section_id.l10n_in_section_tax_ids,
|
||||
key=lambda t: t.amount
|
||||
)
|
||||
updated_tax_ids.append(max_tax.id)
|
||||
else:
|
||||
updated_tax_ids.append(tax.id)
|
||||
if set(line.tax_ids.ids) != set(updated_tax_ids):
|
||||
line.write({'tax_ids': [Command.clear()] + [Command.set(updated_tax_ids)]})
|
||||
|
||||
def _get_l10n_in_invalid_tax_lines(self):
|
||||
self.ensure_one()
|
||||
if self.country_code == 'IN' and not self.commercial_partner_id.l10n_in_pan_entity_id:
|
||||
lines = self.env['account.move.line']
|
||||
for line in self.invoice_line_ids:
|
||||
for tax in line.tax_ids:
|
||||
if (
|
||||
tax.l10n_in_tax_type == 'tcs'
|
||||
and tax.amount != max(tax.l10n_in_section_id.l10n_in_section_tax_ids, key=lambda t: abs(t.amount)).amount
|
||||
):
|
||||
lines |= line._origin
|
||||
return lines
|
||||
|
||||
def _get_sections_aggregate_sum_by_pan(self, section_alert, commercial_partner_id):
|
||||
self.ensure_one()
|
||||
month_start_date, month_end_date = get_month(self.date)
|
||||
company_fiscalyear_dates = self.company_id.sudo().compute_fiscalyear_dates(self.date)
|
||||
fiscalyear_start_date, fiscalyear_end_date = company_fiscalyear_dates['date_from'], company_fiscalyear_dates['date_to']
|
||||
default_domain = [
|
||||
('account_id.l10n_in_tds_tcs_section_id', '=', section_alert.id),
|
||||
('move_id.move_type', '!=', 'entry'),
|
||||
('company_id.l10n_in_tds_feature', '!=', False),
|
||||
('company_id.l10n_in_tan', '=', self.company_id.l10n_in_tan),
|
||||
('parent_state', '=', 'posted')
|
||||
]
|
||||
if commercial_partner_id.l10n_in_pan_entity_id:
|
||||
default_domain += [('move_id.commercial_partner_id.l10n_in_pan_entity_id', '=', commercial_partner_id.l10n_in_pan_entity_id.id)]
|
||||
else:
|
||||
default_domain += [('move_id.commercial_partner_id', '=', commercial_partner_id.id)]
|
||||
frequency_domains = {
|
||||
'monthly': [('date', '>=', month_start_date), ('date', '<=', month_end_date)],
|
||||
'fiscal_yearly': [('date', '>=', fiscalyear_start_date), ('date', '<=', fiscalyear_end_date)],
|
||||
}
|
||||
aggregate_result = {}
|
||||
for frequency, frequency_domain in frequency_domains.items():
|
||||
query = self.env['account.move.line']._search(default_domain + frequency_domain, bypass_access=True, active_test=False)
|
||||
result = self.env.execute_query_dict(SQL(
|
||||
"""
|
||||
SELECT COALESCE(sum(account_move_line.balance), 0) as balance,
|
||||
COALESCE(sum(account_move_line.price_total * am.invoice_currency_rate), 0) as price_total
|
||||
FROM %s
|
||||
JOIN account_move AS am ON am.id = account_move_line.move_id
|
||||
WHERE %s
|
||||
""",
|
||||
query.from_clause,
|
||||
query.where_clause)
|
||||
)
|
||||
aggregate_result[frequency] = result[0]
|
||||
return aggregate_result
|
||||
|
||||
def _l10n_in_is_warning_applicable(self, section_id):
|
||||
self.ensure_one()
|
||||
match section_id.tax_source_type:
|
||||
case 'tcs':
|
||||
return self.company_id.l10n_in_tcs_feature and self.journal_id.type == 'sale'
|
||||
case 'tds':
|
||||
return (
|
||||
self.company_id.l10n_in_tds_feature
|
||||
and self.journal_id.type == 'purchase'
|
||||
and section_id not in self.l10n_in_withhold_move_ids.filtered(lambda m:
|
||||
m.state == 'posted'
|
||||
).mapped('line_ids.tax_ids.l10n_in_section_id')
|
||||
)
|
||||
case _:
|
||||
return False
|
||||
|
||||
def _get_l10n_in_tds_tcs_applicable_sections(self):
|
||||
def _group_by_section_alert(invoice_lines):
|
||||
group_by_lines = {}
|
||||
for line in invoice_lines:
|
||||
group_key = line.account_id.sudo().l10n_in_tds_tcs_section_id
|
||||
if group_key and not line.company_currency_id.is_zero(line.price_total):
|
||||
group_by_lines.setdefault(group_key, [])
|
||||
group_by_lines[group_key].append(line)
|
||||
return group_by_lines
|
||||
|
||||
def _is_section_applicable(section_alert, threshold_sums, invoice_currency_rate, lines):
|
||||
lines_total = sum(
|
||||
(line.price_total * invoice_currency_rate) if section_alert.consider_amount == 'total_amount' else line.balance
|
||||
for line in lines
|
||||
)
|
||||
if section_alert.is_aggregate_limit:
|
||||
aggregate_period_key = section_alert.consider_amount == 'total_amount' and 'price_total' or 'balance'
|
||||
aggregate_total = threshold_sums.get(section_alert.aggregate_period, {}).get(aggregate_period_key)
|
||||
if self.state == 'draft':
|
||||
aggregate_total += lines_total
|
||||
if aggregate_total > section_alert.aggregate_limit:
|
||||
return True
|
||||
return (
|
||||
section_alert.is_per_transaction_limit
|
||||
and lines_total > section_alert.per_transaction_limit
|
||||
)
|
||||
|
||||
if self.country_code == 'IN' and self.move_type in ['in_invoice', 'out_invoice']:
|
||||
warning = set()
|
||||
commercial_partner_id = self.commercial_partner_id
|
||||
if commercial_partner_id.l10n_in_pan_entity_id.tds_deduction == 'no':
|
||||
invoice_lines = self.invoice_line_ids.filtered(lambda l: l.account_id.l10n_in_tds_tcs_section_id.tax_source_type != 'tds')
|
||||
else:
|
||||
invoice_lines = self.invoice_line_ids
|
||||
existing_section = (
|
||||
self.l10n_in_withhold_move_ids.line_ids + self.line_ids
|
||||
).tax_ids.l10n_in_section_id
|
||||
for section_alert, lines in _group_by_section_alert(invoice_lines).items():
|
||||
if (
|
||||
(section_alert not in existing_section
|
||||
or self._get_tcs_applicable_lines(lines))
|
||||
and self._l10n_in_is_warning_applicable(section_alert)
|
||||
and _is_section_applicable(
|
||||
section_alert,
|
||||
self._get_sections_aggregate_sum_by_pan(
|
||||
section_alert,
|
||||
commercial_partner_id
|
||||
),
|
||||
self.invoice_currency_rate,
|
||||
lines
|
||||
)
|
||||
):
|
||||
warning.add(section_alert.id)
|
||||
return self.env['l10n_in.section.alert'].browse(warning)
|
||||
|
||||
def _get_tcs_applicable_lines(self, lines):
|
||||
tcs_applicable_lines = set()
|
||||
for line in lines:
|
||||
if line.l10n_in_tds_tcs_section_id not in line.tax_ids.l10n_in_section_id:
|
||||
tcs_applicable_lines.add(line.id)
|
||||
return self.env['account.move.line'].browse(tcs_applicable_lines)
|
||||
|
||||
def l10n_in_verify_partner_gstin_status(self):
|
||||
self.ensure_one()
|
||||
return self.with_company(self.company_id).partner_id.action_l10n_in_verify_gstin_status()
|
||||
|
||||
def _get_name_invoice_report(self):
|
||||
self.ensure_one()
|
||||
if self.country_code == 'IN':
|
||||
# TODO: remove the view mode check in master, only for stable releases
|
||||
in_invoice_view = self.env.ref('l10n_in.l10n_in_report_invoice_document_inherit', raise_if_not_found=False)
|
||||
if (in_invoice_view and in_invoice_view.sudo().mode == "primary"):
|
||||
return 'l10n_in.l10n_in_report_invoice_document_inherit'
|
||||
return 'l10n_in.l10n_in_report_invoice_document_inherit'
|
||||
return super()._get_name_invoice_report()
|
||||
|
||||
def _post(self, soft=True):
|
||||
|
|
@ -80,14 +555,12 @@ class AccountMove(models.Model):
|
|||
posted = super()._post(soft)
|
||||
gst_treatment_name_mapping = {k: v for k, v in
|
||||
self._fields['l10n_in_gst_treatment']._description_selection(self.env)}
|
||||
for move in posted.filtered(lambda m: m.country_code == 'IN' and m.is_sale_document()):
|
||||
"""Check state is set in company/sub-unit"""
|
||||
company_unit_partner = move.journal_id.l10n_in_gstin_partner_id or move.journal_id.company_id
|
||||
for move in posted.filtered(lambda m: m.country_code == 'IN' and m.company_id.l10n_in_is_gst_registered and m.is_sale_document()):
|
||||
if move.l10n_in_state_id and not move.l10n_in_state_id.l10n_in_tin:
|
||||
raise UserError(_("Please set a valid TIN Number on the Place of Supply %s", move.l10n_in_state_id.name))
|
||||
if not company_unit_partner.state_id:
|
||||
if not move.company_id.state_id:
|
||||
msg = _("Your company %s needs to have a correct address in order to validate this invoice.\n"
|
||||
"Set the address of your company (Don't forget the State field)") % (company_unit_partner.name)
|
||||
"Set the address of your company (Don't forget the State field)", move.company_id.name)
|
||||
action = {
|
||||
"view_mode": "form",
|
||||
"res_model": "res.company",
|
||||
|
|
@ -96,9 +569,16 @@ class AccountMove(models.Model):
|
|||
"views": [[self.env.ref("base.view_company_form").id, "form"]],
|
||||
}
|
||||
raise RedirectWarning(msg, action, _('Go to Company configuration'))
|
||||
|
||||
move.l10n_in_gstin = move.partner_id.vat
|
||||
if not move.l10n_in_gstin and move.l10n_in_gst_treatment in ['regular', 'composition', 'special_economic_zone', 'deemed_export']:
|
||||
if (
|
||||
not move.l10n_in_gstin
|
||||
and move.l10n_in_gst_treatment in [
|
||||
'regular',
|
||||
'composition',
|
||||
'special_economic_zone',
|
||||
'deemed_export'
|
||||
]
|
||||
):
|
||||
raise ValidationError(_(
|
||||
"Partner %(partner_name)s (%(partner_id)s) GSTIN is required under GST Treatment %(name)s",
|
||||
partner_name=move.partner_id.name,
|
||||
|
|
@ -113,30 +593,163 @@ class AccountMove(models.Model):
|
|||
self.ensure_one()
|
||||
return False
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_l10n_in_except_once_post(self):
|
||||
# Prevent deleting entries once it's posted for Indian Company only
|
||||
if any(m.country_code == 'IN' and m.posted_before for m in self) and not self._context.get('force_delete'):
|
||||
raise UserError(_("To keep the audit trail, you can not delete journal entries once they have been posted.\nInstead, you can cancel the journal entry."))
|
||||
|
||||
def _can_be_unlinked(self):
|
||||
self.ensure_one()
|
||||
return (self.country_code != 'IN' or not self.posted_before) and super()._can_be_unlinked()
|
||||
|
||||
def unlink(self):
|
||||
# Add logger here becouse in api ondelete account.move.line is deleted and we can't get total amount
|
||||
logger_msg = False
|
||||
if any(m.country_code == 'IN' and m.posted_before for m in self):
|
||||
if self._context.get('force_delete'):
|
||||
moves_details = ", ".join("{entry_number} ({move_id}) amount {amount_total} {currency} and partner {partner_name}".format(
|
||||
entry_number=m.name,
|
||||
move_id=m.id,
|
||||
amount_total=m.amount_total,
|
||||
currency=m.currency_id.name,
|
||||
partner_name=m.partner_id.display_name)
|
||||
for m in self)
|
||||
logger_msg = 'Force deleted Journal Entries %s by %s (%s)' % (moves_details, self.env.user.name, self.env.user.id)
|
||||
res = super().unlink()
|
||||
if logger_msg:
|
||||
_logger.info(logger_msg)
|
||||
return res
|
||||
def _generate_qr_code(self, silent_errors=False):
|
||||
self.ensure_one()
|
||||
if self.company_id.country_code == 'IN' and self.company_id.l10n_in_upi_id:
|
||||
payment_url = 'upi://pay?pa=%s&pn=%s&am=%s&tr=%s&tn=%s' % (
|
||||
self.company_id.l10n_in_upi_id,
|
||||
self.company_id.name,
|
||||
self.amount_residual,
|
||||
self.payment_reference or self.name,
|
||||
("Payment for %s" % self.name))
|
||||
barcode = self.env['ir.actions.report'].barcode(barcode_type="QR", value=payment_url, width=120, height=120, quiet=False)
|
||||
return image_data_uri(base64.b64encode(barcode))
|
||||
return super()._generate_qr_code(silent_errors)
|
||||
|
||||
def _l10n_in_get_hsn_summary_table(self):
|
||||
self.ensure_one()
|
||||
base_lines, _tax_lines = self._get_rounded_base_and_tax_lines()
|
||||
display_uom = self.env.user.has_group('uom.group_uom')
|
||||
return self.env['account.tax']._l10n_in_get_hsn_summary_table(base_lines, display_uom)
|
||||
|
||||
def _l10n_in_get_bill_from_irn(self, irn):
|
||||
# TO OVERRIDE
|
||||
return False
|
||||
|
||||
# ------Utils------
|
||||
@api.model
|
||||
def _l10n_in_prepare_tax_details(self):
|
||||
def l10n_in_grouping_key_generator(base_line, tax_data):
|
||||
invl = base_line['record']
|
||||
tax = tax_data['tax']
|
||||
if self.l10n_in_gst_treatment in ('overseas', 'special_economic_zone') and all(
|
||||
self.env.ref("l10n_in.tax_tag_igst") in rl.tag_ids
|
||||
for rl in tax.invoice_repartition_line_ids if rl.repartition_type == 'tax'
|
||||
):
|
||||
tax_data['is_reverse_charge'] = False
|
||||
tag_ids = tax.invoice_repartition_line_ids.tag_ids.ids
|
||||
line_code = "other"
|
||||
xmlid_to_res_id = self.env['ir.model.data']._xmlid_to_res_id
|
||||
if not invl.currency_id.is_zero(tax_data['tax_amount_currency']):
|
||||
if xmlid_to_res_id("l10n_in.tax_tag_cess") in tag_ids:
|
||||
if tax.amount_type != "percent":
|
||||
line_code = "cess_non_advol"
|
||||
else:
|
||||
line_code = "cess"
|
||||
elif xmlid_to_res_id("l10n_in.tax_tag_state_cess") in tag_ids:
|
||||
if tax.amount_type != "percent":
|
||||
line_code = "state_cess_non_advol"
|
||||
else:
|
||||
line_code = "state_cess"
|
||||
else:
|
||||
for gst in ["cgst", "sgst", "igst"]:
|
||||
if xmlid_to_res_id("l10n_in.tax_tag_%s" % (gst)) in tag_ids:
|
||||
# need to separate rc tax value so it's not pass to other values
|
||||
line_code = f'{gst}_rc' if tax_data['is_reverse_charge'] else gst
|
||||
return {
|
||||
"tax": tax,
|
||||
"base_product_id": invl.product_id,
|
||||
"tax_product_id": invl.product_id,
|
||||
"base_product_uom_id": invl.product_uom_id,
|
||||
"tax_product_uom_id": invl.product_uom_id,
|
||||
"line_code": line_code,
|
||||
}
|
||||
|
||||
def l10n_in_filter_to_apply(base_line, tax_values):
|
||||
return base_line['record'].display_type != 'rounding'
|
||||
|
||||
return self._prepare_invoice_aggregated_taxes(
|
||||
filter_tax_values_to_apply=l10n_in_filter_to_apply,
|
||||
grouping_key_generator=l10n_in_grouping_key_generator,
|
||||
)
|
||||
|
||||
def _get_l10n_in_seller_buyer_party(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
"seller_details": self.company_id.partner_id,
|
||||
"dispatch_details": self._l10n_in_get_warehouse_address() or self.company_id.partner_id,
|
||||
"buyer_details": self.partner_id,
|
||||
"ship_to_details": self.partner_shipping_id or self.partner_id
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _l10n_in_extract_digits(self, string):
|
||||
if not string:
|
||||
return ""
|
||||
matches = re.findall(r"\d+", string)
|
||||
return "".join(matches)
|
||||
|
||||
@api.model
|
||||
def _l10n_in_is_service_hsn(self, hsn_code):
|
||||
return self._l10n_in_extract_digits(hsn_code).startswith('99')
|
||||
|
||||
@api.model
|
||||
def _l10n_in_round_value(self, amount, precision_digits=2):
|
||||
"""
|
||||
This method is call for rounding.
|
||||
If anything is wrong with rounding then we quick fix in method
|
||||
"""
|
||||
return json_float_round(amount, precision_digits)
|
||||
|
||||
@api.model
|
||||
def _get_l10n_in_tax_details_by_line_code(self, tax_details):
|
||||
l10n_in_tax_details = {}
|
||||
for tax_detail in tax_details.values():
|
||||
if tax_detail["tax"].l10n_in_reverse_charge:
|
||||
l10n_in_tax_details.setdefault("is_reverse_charge", True)
|
||||
line_code = tax_detail["line_code"]
|
||||
l10n_in_tax_details.setdefault("%s_rate" % (line_code), tax_detail["tax"].amount)
|
||||
l10n_in_tax_details.setdefault("%s_amount" % (line_code), 0.00)
|
||||
l10n_in_tax_details.setdefault("%s_amount_currency" % (line_code), 0.00)
|
||||
l10n_in_tax_details["%s_amount" % (line_code)] += tax_detail["tax_amount"]
|
||||
l10n_in_tax_details["%s_amount_currency" % (line_code)] += tax_detail["tax_amount_currency"]
|
||||
return l10n_in_tax_details
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_get_iap_buy_credits_message(self):
|
||||
url = self.env['iap.account'].get_credits_url(service_name=IAP_SERVICE_NAME)
|
||||
return Markup("""<p><b>%s</b></p><p>%s <a href="%s">%s</a></p>""") % (
|
||||
_("You have insufficient credits to send this document!"),
|
||||
_("Please buy more credits and retry: "),
|
||||
url,
|
||||
_("Buy Credits")
|
||||
)
|
||||
|
||||
def _get_sync_stack(self, container):
|
||||
stack, update_containers = super()._get_sync_stack(container)
|
||||
if all(move.country_code != 'IN' for move in self):
|
||||
return stack, update_containers
|
||||
_tax_container, invoice_container, misc_container = update_containers()
|
||||
moves = invoice_container['records'] + misc_container['records']
|
||||
stack.append((9, self._sync_l10n_in_gstr_section(moves)))
|
||||
return stack, update_containers
|
||||
|
||||
@contextmanager
|
||||
def _sync_l10n_in_gstr_section(self, moves):
|
||||
yield
|
||||
tax_tags_dict = self.env['account.move.line']._get_l10n_in_tax_tag_ids()
|
||||
# we set the section on the invoice lines
|
||||
moves.line_ids._set_l10n_in_gstr_section(tax_tags_dict)
|
||||
|
||||
def _get_l10n_in_invoice_label(self):
|
||||
self.ensure_one()
|
||||
exempt_types = {'exempt', 'nil_rated', 'non_gst'}
|
||||
if self.country_code != 'IN' or not self.is_sale_document(include_receipts=False):
|
||||
return
|
||||
gst_treatment = self.l10n_in_gst_treatment
|
||||
company = self.company_id
|
||||
tax_types = set(self.invoice_line_ids.tax_ids.mapped('l10n_in_tax_type'))
|
||||
if company.l10n_in_is_gst_registered and tax_types:
|
||||
if gst_treatment in ['overseas', 'special_economic_zone']:
|
||||
return 'Tax Invoice'
|
||||
elif tax_types.issubset(exempt_types):
|
||||
return 'Bill of Supply'
|
||||
elif tax_types.isdisjoint(exempt_types):
|
||||
return 'Tax Invoice'
|
||||
elif gst_treatment in ['unregistered', 'consumer']:
|
||||
return 'Invoice-cum-Bill of Supply'
|
||||
return 'Invoice'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
from odoo import models
|
||||
|
||||
|
||||
class AccountJournal(models.Model):
|
||||
_inherit = "account.journal"
|
||||
|
||||
def _update_payment_method_lines(self, payment_type):
|
||||
bank_journals = self.filtered(lambda j: j.type == "bank" and j.company_id.chart_template == "in")
|
||||
if not bank_journals:
|
||||
return
|
||||
|
||||
if payment_type == 'inbound':
|
||||
account_xmlid = "account_journal_payment_debit_account_id"
|
||||
else:
|
||||
account_xmlid = "account_journal_payment_credit_account_id"
|
||||
|
||||
lines_to_update = bank_journals[f"{payment_type}_payment_method_line_ids"].filtered(
|
||||
lambda l: l.payment_method_id.code == 'manual'
|
||||
)
|
||||
for company, lines in lines_to_update.grouped('company_id').items():
|
||||
if account := self.env['account.chart.template'].with_company(company).ref(account_xmlid, raise_if_not_found=False):
|
||||
lines.payment_account_id = account
|
||||
|
||||
def _compute_inbound_payment_method_line_ids(self):
|
||||
super()._compute_inbound_payment_method_line_ids()
|
||||
self._update_payment_method_lines("inbound")
|
||||
|
||||
def _compute_outbound_payment_method_line_ids(self):
|
||||
super()._compute_outbound_payment_method_line_ids()
|
||||
self._update_payment_method_lines("outbound")
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
import re
|
||||
from datetime import date
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
l10n_in_hsn_code = fields.Char(string="HSN/SAC Code", compute="_compute_l10n_in_hsn_code", store=True, readonly=False, copy=False)
|
||||
l10n_in_gstr_section = fields.Selection(
|
||||
selection=[
|
||||
("sale_b2b_rcm", "B2B RCM"),
|
||||
("sale_b2b_regular", "B2B Regular"),
|
||||
("sale_b2cl", "B2CL"),
|
||||
("sale_b2cs", "B2CS"),
|
||||
("sale_exp_wp", "EXP(WP)"),
|
||||
("sale_exp_wop", "EXP(WOP)"),
|
||||
("sale_sez_wp", "SEZ(WP)"),
|
||||
("sale_sez_wop", "SEZ(WOP)"),
|
||||
("sale_deemed_export", "Deemed Export"),
|
||||
("sale_cdnr_rcm", "CDNR RCM"),
|
||||
("sale_cdnr_regular", "CDNR Regular"),
|
||||
("sale_cdnr_deemed_export", "CDNR(Deemed Export)"),
|
||||
("sale_cdnr_sez_wp", "CDNR(SEZ-WP)"),
|
||||
("sale_cdnr_sez_wop", "CDNR(SEZ-WOP)"),
|
||||
("sale_cdnur_b2cl", "CDNUR(B2CL)"),
|
||||
("sale_cdnur_exp_wp", "CDNUR(EXP-WP)"),
|
||||
("sale_cdnur_exp_wop", "CDNUR(EXP-WOP)"),
|
||||
("sale_nil_rated", "Nil Rated"),
|
||||
("sale_exempt", "Exempt"),
|
||||
("sale_non_gst_supplies", "Non-GST Supplies"),
|
||||
("sale_eco_9_5", "ECO 9(5)"),
|
||||
("sale_out_of_scope", "Out of Scope"),
|
||||
("purchase_b2b_regular", "B2B Regular"),
|
||||
("purchase_b2c_regular", "B2C Regular"), # will be removed in master
|
||||
("purchase_b2b_rcm", "B2B RCM"),
|
||||
("purchase_b2c_rcm", "B2C RCM"),
|
||||
("purchase_imp_services", "IMP(service)"),
|
||||
("purchase_imp_goods", "IMP(goods)"),
|
||||
("purchase_cdnr_regular", "CDNR Regular"),
|
||||
("purchase_cdnur_regular", "CDNUR Regular"),
|
||||
("purchase_cdnr_rcm", "CDNR RCM"),
|
||||
("purchase_cdnur_rcm", "CDNUR RCM"),
|
||||
("purchase_nil_rated", "Nil Rated"),
|
||||
("purchase_exempt", "Exempt"),
|
||||
("purchase_non_gst_supplies", "Non-GST Supplies"),
|
||||
("purchase_out_of_scope", "Out of Scope"),
|
||||
],
|
||||
string="GSTR Section",
|
||||
index="btree_not_null",
|
||||
)
|
||||
|
||||
# withholding related fields
|
||||
l10n_in_withhold_tax_amount = fields.Monetary(string="TDS Tax Amount", compute='_compute_l10n_in_withhold_tax_amount')
|
||||
l10n_in_tds_tcs_section_id = fields.Many2one(related="account_id.l10n_in_tds_tcs_section_id")
|
||||
|
||||
@api.depends('tax_ids')
|
||||
def _compute_l10n_in_withhold_tax_amount(self):
|
||||
# Compute the withhold tax amount for the withholding lines
|
||||
withholding_lines = self.filtered('move_id.l10n_in_is_withholding')
|
||||
(self - withholding_lines).l10n_in_withhold_tax_amount = False
|
||||
for line in withholding_lines:
|
||||
line.l10n_in_withhold_tax_amount = line.currency_id.round(abs(line.price_total - line.price_subtotal))
|
||||
|
||||
@api.depends('product_id', 'product_id.l10n_in_hsn_code')
|
||||
def _compute_l10n_in_hsn_code(self):
|
||||
for line in self:
|
||||
if line.move_id.country_code == 'IN' and line.parent_state == 'draft':
|
||||
line.l10n_in_hsn_code = line.product_id.l10n_in_hsn_code
|
||||
|
||||
def _l10n_in_check_invalid_hsn_code(self):
|
||||
self.ensure_one()
|
||||
hsn_code = self.env['account.move']._l10n_in_extract_digits(self.l10n_in_hsn_code)
|
||||
if not hsn_code:
|
||||
return _("HSN code is not set in product line %(name)s", name=self.name)
|
||||
elif not re.match(r'^\d{4}$|^\d{6}$|^\d{8}$', hsn_code):
|
||||
return _(
|
||||
"Invalid HSN Code (%(hsn_code)s) in product line %(product_line)s",
|
||||
hsn_code=hsn_code,
|
||||
product_line=self.product_id.name or self.name
|
||||
)
|
||||
return False
|
||||
|
||||
def _get_l10n_in_tax_tag_ids(self):
|
||||
xmlid_to_res_id = self.env['ir.model.data']._xmlid_to_res_id
|
||||
tag_refs = {
|
||||
'sgst': ['l10n_in.tax_tag_base_sgst', 'l10n_in.tax_tag_sgst'],
|
||||
'cgst': ['l10n_in.tax_tag_base_cgst', 'l10n_in.tax_tag_cgst'],
|
||||
'igst': ['l10n_in.tax_tag_base_igst', 'l10n_in.tax_tag_igst'],
|
||||
'cess': ['l10n_in.tax_tag_base_cess', 'l10n_in.tax_tag_cess'],
|
||||
'eco_9_5': ['l10n_in.tax_tag_eco_9_5'],
|
||||
}
|
||||
return {
|
||||
categ: [xmlid_to_res_id(xml_id) for xml_id in ref]
|
||||
for categ, ref in tag_refs.items()
|
||||
}
|
||||
|
||||
def _get_l10n_in_gstr_section(self, tax_tags_dict):
|
||||
|
||||
def tags_have_categ(line_tax_tags, categories):
|
||||
return any(tag in line_tax_tags for category in categories for tag in tax_tags_dict.get(category, []))
|
||||
|
||||
def is_invoice(move):
|
||||
return move.is_inbound() and not move.debit_origin_id
|
||||
|
||||
def is_move_bill(move):
|
||||
return move.is_outbound() and not move.debit_origin_id
|
||||
|
||||
def get_transaction_type(move):
|
||||
return 'intra_state' if move.l10n_in_state_id == move.company_id.state_id else 'inter_state'
|
||||
|
||||
def is_reverse_charge_tax(line):
|
||||
return any(tax.l10n_in_reverse_charge for tax in line.tax_ids | line.tax_line_id)
|
||||
|
||||
def is_lut_tax(line):
|
||||
return any(tax.l10n_in_is_lut for tax in line.tax_ids | line.tax_line_id)
|
||||
|
||||
def get_sales_section(line):
|
||||
move = line.move_id
|
||||
gst_treatment = move.l10n_in_gst_treatment
|
||||
transaction_type = get_transaction_type(move)
|
||||
line_tags = line.tax_tag_ids.ids
|
||||
is_inv = is_invoice(move)
|
||||
amt_limit = 100000 if not line.invoice_date or line.invoice_date >= date(2024, 11, 1) else 250000
|
||||
|
||||
# ECO 9(5) Section: Check if the line has the ECO 9(5) tax tag
|
||||
if tags_have_categ(line_tags, ['eco_9_5']):
|
||||
return 'sale_eco_9_5'
|
||||
|
||||
# Nil rated, Exempt, Non-GST Sales
|
||||
if gst_treatment != 'overseas':
|
||||
if any(tax.l10n_in_tax_type == 'nil_rated' for tax in line.tax_ids):
|
||||
return 'sale_nil_rated'
|
||||
elif any(tax.l10n_in_tax_type == 'exempt' for tax in line.tax_ids):
|
||||
return 'sale_exempt'
|
||||
elif any(tax.l10n_in_tax_type == 'non_gst' for tax in line.tax_ids):
|
||||
return 'sale_non_gst_supplies'
|
||||
|
||||
# B2CS: Unregistered or Consumer sales with gst tags
|
||||
if gst_treatment in ('unregistered', 'consumer') and not is_reverse_charge_tax(line):
|
||||
if (transaction_type == 'intra_state' and tags_have_categ(line_tags, ['sgst', 'cgst', 'cess'])) or (
|
||||
transaction_type == "inter_state"
|
||||
and tags_have_categ(line_tags, ['igst', 'cess'])
|
||||
and not is_lut_tax(line)
|
||||
and (
|
||||
is_inv and move.amount_total <= amt_limit
|
||||
or move.debit_origin_id and move.debit_origin_id.amount_total <= amt_limit
|
||||
or move.reversed_entry_id and move.reversed_entry_id.amount_total <= amt_limit
|
||||
)
|
||||
):
|
||||
return 'sale_b2cs'
|
||||
|
||||
# If no relevant tags are found, or the tags do not match any category, mark as out of scope
|
||||
if not line_tags or not tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess', 'eco_9_5']):
|
||||
return 'sale_out_of_scope'
|
||||
|
||||
# If it's a standard invoice (not a debit/credit note)
|
||||
if is_inv:
|
||||
# B2B with Reverse Charge and Regular
|
||||
if gst_treatment in ('regular', 'composition', 'uin_holders') and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess']) and not is_lut_tax(line):
|
||||
if is_reverse_charge_tax(line):
|
||||
return 'sale_b2b_rcm'
|
||||
return 'sale_b2b_regular'
|
||||
|
||||
if not is_reverse_charge_tax(line):
|
||||
# B2CL: Unregistered interstate sales above threshold
|
||||
if (
|
||||
gst_treatment in ('unregistered', 'consumer')
|
||||
and tags_have_categ(line_tags, ['igst', 'cess'])
|
||||
and not is_lut_tax(line)
|
||||
and transaction_type == 'inter_state'
|
||||
and move.amount_total > amt_limit
|
||||
):
|
||||
return 'sale_b2cl'
|
||||
# Export with payment and without payment (under LUT) of tax
|
||||
if gst_treatment == 'overseas' and tags_have_categ(line_tags, ['igst', 'cess']):
|
||||
if is_lut_tax(line):
|
||||
return 'sale_exp_wop'
|
||||
return 'sale_exp_wp'
|
||||
# SEZ with payment and without payment of tax
|
||||
if gst_treatment == 'special_economic_zone' and tags_have_categ(line_tags, ['igst', 'cess']):
|
||||
if is_lut_tax(line):
|
||||
return 'sale_sez_wop'
|
||||
return 'sale_sez_wp'
|
||||
# Deemed export
|
||||
if gst_treatment == 'deemed_export' and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess']) and not is_lut_tax(line):
|
||||
return 'sale_deemed_export'
|
||||
|
||||
# If it's not a standard invoice (i.e., it's a debit/credit note)
|
||||
if not is_inv:
|
||||
# CDN for B2B reverse charge and B2B regular
|
||||
if gst_treatment in ('regular', 'composition', 'uin_holders') and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess']) and not is_lut_tax(line):
|
||||
if is_reverse_charge_tax(line):
|
||||
return 'sale_cdnr_rcm'
|
||||
return 'sale_cdnr_regular'
|
||||
if not is_reverse_charge_tax(line):
|
||||
# CDN for SEZ exports with payment and without payment
|
||||
if gst_treatment == 'special_economic_zone' and tags_have_categ(line_tags, ['igst', 'cess']):
|
||||
if is_lut_tax(line):
|
||||
return 'sale_cdnr_sez_wop'
|
||||
return 'sale_cdnr_sez_wp'
|
||||
# CDN for deemed exports
|
||||
if gst_treatment == 'deemed_export' and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess']) and not is_lut_tax(line):
|
||||
return 'sale_cdnr_deemed_export'
|
||||
# CDN for B2CL (interstate > threshold)
|
||||
if (
|
||||
gst_treatment in ('unregistered', 'consumer')
|
||||
and tags_have_categ(line_tags, ['igst', 'cess'])
|
||||
and not is_lut_tax(line)
|
||||
and transaction_type == 'inter_state'
|
||||
and (
|
||||
move.debit_origin_id and move.debit_origin_id.amount_total > amt_limit
|
||||
or move.reversed_entry_id and move.reversed_entry_id.amount_total > amt_limit
|
||||
or not move.reversed_entry_id and not move.is_inbound()
|
||||
)
|
||||
):
|
||||
return 'sale_cdnur_b2cl'
|
||||
# CDN for exports with payment and without payment
|
||||
if gst_treatment == 'overseas' and tags_have_categ(line_tags, ['igst', 'cess']):
|
||||
if is_lut_tax(line):
|
||||
return 'sale_cdnur_exp_wop'
|
||||
return 'sale_cdnur_exp_wp'
|
||||
# If none of the above match, default to out of scope
|
||||
return 'sale_out_of_scope'
|
||||
|
||||
def get_purchase_section(line):
|
||||
move = line.move_id
|
||||
gst_treatment = move.l10n_in_gst_treatment
|
||||
line_tags = line.tax_tag_ids.ids
|
||||
is_bill = is_move_bill(move)
|
||||
|
||||
# Nil rated, Exempt, Non-GST purchases
|
||||
if gst_treatment != 'overseas':
|
||||
if any(tax.l10n_in_tax_type == 'nil_rated' for tax in line.tax_ids):
|
||||
return 'purchase_nil_rated'
|
||||
elif any(tax.l10n_in_tax_type == 'exempt' for tax in line.tax_ids):
|
||||
return 'purchase_exempt'
|
||||
elif any(tax.l10n_in_tax_type == 'non_gst' for tax in line.tax_ids):
|
||||
return 'purchase_non_gst_supplies'
|
||||
|
||||
# If no relevant tags are found, or the tags do not match any category, mark as out of scope
|
||||
if not line_tags or not tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess']):
|
||||
return 'purchase_out_of_scope'
|
||||
|
||||
if is_bill:
|
||||
# B2B Regular and Reverse Charge purchases
|
||||
if (gst_treatment in ('regular', 'composition', 'uin_holders') and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess'])):
|
||||
if is_reverse_charge_tax(line):
|
||||
return 'purchase_b2b_rcm'
|
||||
return 'purchase_b2b_regular'
|
||||
|
||||
if not is_reverse_charge_tax(line) and (
|
||||
gst_treatment == 'deemed_export' and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess'])
|
||||
or gst_treatment == 'special_economic_zone' and tags_have_categ(line_tags, ['igst', 'cess'])
|
||||
):
|
||||
return 'purchase_b2b_regular'
|
||||
|
||||
# B2C Unregistered or Consumer sales with gst tags
|
||||
if gst_treatment in ('unregistered', 'consumer') and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess']) and is_reverse_charge_tax(line):
|
||||
return 'purchase_b2c_rcm'
|
||||
|
||||
# export service type products purchases
|
||||
if gst_treatment == 'overseas' and any(tax.tax_scope == 'service' for tax in line.tax_ids | line.tax_line_id) and tags_have_categ(line_tags, ['igst', 'cess']):
|
||||
return 'purchase_imp_services'
|
||||
|
||||
# export goods type products purchases
|
||||
if gst_treatment == 'overseas' and tags_have_categ(line_tags, ['igst', 'cess']) and not is_reverse_charge_tax(line):
|
||||
return 'purchase_imp_goods'
|
||||
|
||||
if not is_bill:
|
||||
# credit notes for b2b purchases
|
||||
if gst_treatment in ('regular', 'composition', 'uin_holders') and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess']):
|
||||
if is_reverse_charge_tax(line):
|
||||
return 'purchase_cdnr_rcm'
|
||||
return 'purchase_cdnr_regular'
|
||||
|
||||
# credit notes for b2c purchases
|
||||
if gst_treatment in ('unregistered', 'consumer') and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess']) and is_reverse_charge_tax(line):
|
||||
return 'purchase_cdnur_rcm'
|
||||
|
||||
if not is_reverse_charge_tax(line):
|
||||
if gst_treatment == 'deemed_export' and tags_have_categ(line_tags, ['sgst', 'cgst', 'igst', 'cess'])\
|
||||
or gst_treatment == 'special_economic_zone' and tags_have_categ(line_tags, ['igst', 'cess']):
|
||||
return 'purchase_cdnr_regular'
|
||||
|
||||
if gst_treatment == 'overseas' and tags_have_categ(line_tags, ['igst', 'cess']):
|
||||
return 'purchase_cdnur_regular'
|
||||
|
||||
# If none of the above match, default to out of scope
|
||||
return 'purchase_out_of_scope'
|
||||
|
||||
indian_sale_moves_lines = self.filtered(
|
||||
lambda l: l.move_id.country_code == 'IN'
|
||||
and l.move_id.is_sale_document(include_receipts=True)
|
||||
and l.display_type in ('product', 'tax')
|
||||
)
|
||||
indian_moves_purchase_lines = self.filtered(
|
||||
lambda l: l.move_id.country_code == 'IN'
|
||||
and l.move_id.is_purchase_document(include_receipts=True)
|
||||
and l.display_type in ('product', 'tax')
|
||||
)
|
||||
# No Indian sale or purchase lines to process
|
||||
if not indian_sale_moves_lines and not indian_moves_purchase_lines:
|
||||
return {}
|
||||
|
||||
move_lines_by_gstr_section = {
|
||||
**indian_sale_moves_lines.grouped(get_sales_section),
|
||||
**indian_moves_purchase_lines.grouped(get_purchase_section),
|
||||
}
|
||||
|
||||
return move_lines_by_gstr_section
|
||||
|
||||
def _set_l10n_in_gstr_section(self, tax_tags_dict):
|
||||
move_lines_by_gstr_section = self._get_l10n_in_gstr_section(tax_tags_dict)
|
||||
if move_lines_by_gstr_section:
|
||||
for gstr_section, move_lines in move_lines_by_gstr_section.items():
|
||||
move_lines.l10n_in_gstr_section = gstr_section
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountPayment(models.Model):
|
||||
_inherit = "account.payment"
|
||||
|
||||
# withholding related fields
|
||||
l10n_in_withhold_move_ids = fields.One2many(
|
||||
'account.move', 'l10n_in_withholding_ref_payment_id',
|
||||
string="Indian Payment TDS Entries",
|
||||
)
|
||||
l10n_in_total_withholding_amount = fields.Monetary(compute='_compute_l10n_in_total_withholding_amount')
|
||||
l10n_in_tds_feature_enabled = fields.Boolean(related='company_id.l10n_in_tds_feature')
|
||||
|
||||
def _compute_l10n_in_total_withholding_amount(self):
|
||||
for payment in self:
|
||||
if payment.company_id.l10n_in_tds_feature:
|
||||
payment.l10n_in_total_withholding_amount = sum(payment.l10n_in_withhold_move_ids.filtered(
|
||||
lambda m: m.state == 'posted').l10n_in_withholding_line_ids.mapped('l10n_in_withhold_tax_amount'))
|
||||
else:
|
||||
payment.l10n_in_total_withholding_amount = 0.0
|
||||
|
||||
def action_l10n_in_withholding_entries(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': "TDS Entries",
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'account.move',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('id', 'in', self.l10n_in_withhold_move_ids.ids)],
|
||||
}
|
||||
143
odoo-bringout-oca-ocb-l10n_in/l10n_in/models/account_tax.py
Normal file
143
odoo-bringout-oca-ocb-l10n_in/l10n_in/models/account_tax.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
from collections import defaultdict
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import frozendict
|
||||
|
||||
|
||||
class AccountTax(models.Model):
|
||||
_inherit = 'account.tax'
|
||||
|
||||
l10n_in_reverse_charge = fields.Boolean("Reverse charge", help="Tick this if this tax is reverse charge. Only for Indian accounting")
|
||||
l10n_in_gst_tax_type = fields.Selection(
|
||||
selection=[('igst', 'igst'), ('cgst', 'cgst'), ('sgst', 'sgst'), ('cess', 'cess')],
|
||||
compute='_compute_l10n_in_gst_tax_type',
|
||||
)
|
||||
l10n_in_is_lut = fields.Boolean(
|
||||
string="LUT",
|
||||
help="Tick this if this tax is used in LUT (Letter of Undertaking) transactions. Only for Indian accounting.",
|
||||
)
|
||||
l10n_in_tax_type = fields.Selection(
|
||||
selection=[
|
||||
('gst', 'GST'),
|
||||
('tcs', 'TCS'),
|
||||
('tds_sale', 'TDS Sale'),
|
||||
('tds_purchase', 'TDS Purchase'),
|
||||
('nil_rated', 'Nil Rated'),
|
||||
('exempt', 'Exempt'),
|
||||
('non_gst', 'Non-GST'),
|
||||
],
|
||||
string="Indian Tax Type",
|
||||
)
|
||||
|
||||
# withholding related fields
|
||||
l10n_in_section_id = fields.Many2one('l10n_in.section.alert', string="Section")
|
||||
l10n_in_tds_feature_enabled = fields.Boolean(related='company_id.l10n_in_tds_feature')
|
||||
l10n_in_tcs_feature_enabled = fields.Boolean(related='company_id.l10n_in_tcs_feature')
|
||||
|
||||
@api.depends('country_code', 'invoice_repartition_line_ids.tag_ids')
|
||||
def _compute_l10n_in_gst_tax_type(self):
|
||||
self.l10n_in_gst_tax_type = False
|
||||
in_taxes = self.filtered(lambda tax: tax.country_code == 'IN')
|
||||
if in_taxes:
|
||||
tags_mapping = {
|
||||
'igst': self.env.ref('l10n_in.tax_tag_igst'),
|
||||
'cgst': self.env.ref('l10n_in.tax_tag_cgst'),
|
||||
'sgst': self.env.ref('l10n_in.tax_tag_sgst'),
|
||||
'cess': self.env.ref('l10n_in.tax_tag_cess'),
|
||||
}
|
||||
for tax in in_taxes:
|
||||
tags = tax.invoice_repartition_line_ids.tag_ids
|
||||
for tag_code, tag in tags_mapping.items():
|
||||
if tag in tags:
|
||||
tax.l10n_in_gst_tax_type = tag_code
|
||||
break
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# HELPERS IN BOTH PYTHON/JAVASCRIPT (hsn_summary.js / account_tax.py)
|
||||
|
||||
# HSN SUMMARY
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def _prepare_base_line_for_taxes_computation(self, record, **kwargs):
|
||||
# EXTENDS 'account'
|
||||
results = super()._prepare_base_line_for_taxes_computation(record, **kwargs)
|
||||
results['l10n_in_hsn_code'] = self._get_base_line_field_value_from_record(record, 'l10n_in_hsn_code', kwargs, False)
|
||||
return results
|
||||
|
||||
@api.model
|
||||
def _l10n_in_get_hsn_summary_table(self, base_lines, display_uom):
|
||||
l10n_in_gst_tax_types = set()
|
||||
items_map = defaultdict(lambda: {
|
||||
'quantity': 0.0,
|
||||
'amount_untaxed': 0.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
})
|
||||
|
||||
def get_base_line_grouping_key(base_line):
|
||||
unique_taxes_data = set(
|
||||
tax_data['tax']
|
||||
for tax_data in base_line['tax_details']['taxes_data']
|
||||
if tax_data['tax']['l10n_in_gst_tax_type'] in ('igst', 'cgst', 'sgst')
|
||||
)
|
||||
rate = sum(tax.amount for tax in unique_taxes_data)
|
||||
|
||||
return {
|
||||
'l10n_in_hsn_code': base_line['l10n_in_hsn_code'],
|
||||
'uom_name': base_line['product_uom_id'].name,
|
||||
'rate': rate,
|
||||
}
|
||||
|
||||
# quantity / amount_untaxed.
|
||||
for base_line in base_lines:
|
||||
key = frozendict(get_base_line_grouping_key(base_line))
|
||||
if not key['l10n_in_hsn_code']:
|
||||
continue
|
||||
|
||||
item = items_map[key]
|
||||
item['quantity'] += base_line['quantity']
|
||||
item['amount_untaxed'] += (
|
||||
base_line['tax_details']['total_excluded_currency']
|
||||
+ base_line['tax_details']['delta_total_excluded_currency']
|
||||
)
|
||||
|
||||
# Tax amounts.
|
||||
def grouping_function(base_line, tax_data):
|
||||
return {
|
||||
**get_base_line_grouping_key(base_line),
|
||||
'l10n_in_gst_tax_type': tax_data['tax'].l10n_in_gst_tax_type,
|
||||
} if tax_data else None
|
||||
|
||||
base_lines_aggregated_values = self._aggregate_base_lines_tax_details(base_lines, grouping_function)
|
||||
values_per_grouping_key = self._aggregate_base_lines_aggregated_values(base_lines_aggregated_values)
|
||||
for grouping_key, values in values_per_grouping_key.items():
|
||||
if (
|
||||
not grouping_key
|
||||
or not grouping_key['l10n_in_hsn_code']
|
||||
or not grouping_key['l10n_in_gst_tax_type']
|
||||
):
|
||||
continue
|
||||
|
||||
key = frozendict({
|
||||
'l10n_in_hsn_code': grouping_key['l10n_in_hsn_code'],
|
||||
'rate': grouping_key['rate'],
|
||||
'uom_name': grouping_key['uom_name'],
|
||||
})
|
||||
item = items_map[key]
|
||||
l10n_in_gst_tax_type = grouping_key['l10n_in_gst_tax_type']
|
||||
item[f'tax_amount_{l10n_in_gst_tax_type}'] += values['tax_amount_currency']
|
||||
l10n_in_gst_tax_types.add(l10n_in_gst_tax_type)
|
||||
|
||||
return {
|
||||
'has_igst': 'igst' in l10n_in_gst_tax_types,
|
||||
'has_gst': bool({'cgst', 'sgst'} & l10n_in_gst_tax_types),
|
||||
'has_cess': 'cess' in l10n_in_gst_tax_types,
|
||||
'nb_columns': 5 + len(l10n_in_gst_tax_types),
|
||||
'display_uom': display_uom,
|
||||
'items': [
|
||||
key | values
|
||||
for key, values in items_map.items()
|
||||
],
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class AccountChartTemplate(models.Model):
|
||||
_inherit = 'account.chart.template'
|
||||
|
||||
def _prepare_all_journals(self, acc_template_ref, company, journals_dict=None):
|
||||
res = super(AccountChartTemplate, self)._prepare_all_journals(acc_template_ref, company, journals_dict=journals_dict)
|
||||
if self == self.env.ref('l10n_in.indian_chart_template_standard'):
|
||||
for journal in res:
|
||||
if journal.get('type') in ('sale','purchase'):
|
||||
journal['l10n_in_gstin_partner_id'] = company.partner_id.id
|
||||
return res
|
||||
|
||||
def _load(self, company):
|
||||
res = super(AccountChartTemplate, self)._load(company)
|
||||
if self == self.env.ref("l10n_in.indian_chart_template_standard"):
|
||||
company.write({
|
||||
'account_opening_date': fields.Date.context_today(self).replace(month=4, day=1),
|
||||
'fiscalyear_last_month': '3',
|
||||
})
|
||||
return res
|
||||
|
||||
|
||||
class AccountTaxTemplate(models.Model):
|
||||
_inherit = 'account.tax.template'
|
||||
|
||||
l10n_in_reverse_charge = fields.Boolean("Reverse charge", help="Tick this if this tax is reverse charge. Only for Indian accounting")
|
||||
|
||||
def _get_tax_vals(self, company, tax_template_to_tax):
|
||||
val = super(AccountTaxTemplate, self)._get_tax_vals(company, tax_template_to_tax)
|
||||
if self.tax_group_id:
|
||||
val['l10n_in_reverse_charge'] = self.l10n_in_reverse_charge
|
||||
return val
|
||||
195
odoo-bringout-oca-ocb-l10n_in/l10n_in/models/company.py
Normal file
195
odoo-bringout-oca-ocb-l10n_in/l10n_in/models/company.py
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
import pytz
|
||||
from stdnum.in_ import pan, gstin
|
||||
|
||||
from odoo import Command, _, api, fields, models
|
||||
from odoo.exceptions import RedirectWarning
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
l10n_in_upi_id = fields.Char(string="UPI Id")
|
||||
l10n_in_hsn_code_digit = fields.Selection(
|
||||
selection=[
|
||||
("4", "4 Digits (turnover < 5 CR.)"),
|
||||
("6", "6 Digits (turnover > 5 CR.)"),
|
||||
("8", "8 Digits"),
|
||||
],
|
||||
string="HSN Code Digit",
|
||||
compute="_compute_l10n_in_hsn_code_digit",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
l10n_in_edi_production_env = fields.Boolean(
|
||||
string="Indian Production Environment",
|
||||
help="Enable the use of production credentials",
|
||||
groups="base.group_system",
|
||||
default=True,
|
||||
)
|
||||
l10n_in_pan_entity_id = fields.Many2one(
|
||||
related="partner_id.l10n_in_pan_entity_id",
|
||||
string="PAN",
|
||||
store=True,
|
||||
readonly=False,
|
||||
help="PAN enables the department to link all transactions of the person with the department.\n"
|
||||
"These transactions include taxpayments, TDS/TCS credits, returns of income/wealth/gift/FBT,"
|
||||
"specified transactions, correspondence, and so on.\n"
|
||||
"Thus, PAN acts as an identifier for the person with the tax department.",
|
||||
)
|
||||
l10n_in_pan_type = fields.Selection(related="l10n_in_pan_entity_id.type", string="PAN Type")
|
||||
l10n_in_tan = fields.Char(related="partner_id.l10n_in_tan", string="TAN", readonly=False)
|
||||
l10n_in_gst_state_warning = fields.Char(related="partner_id.l10n_in_gst_state_warning")
|
||||
|
||||
# TDS/TCS settings
|
||||
l10n_in_tds_feature = fields.Boolean(
|
||||
string="TDS",
|
||||
compute="_compute_l10n_in_parent_based_features",
|
||||
inverse="_inverse_l10n_in_tds_feature",
|
||||
recursive=True,
|
||||
store=True,
|
||||
)
|
||||
l10n_in_tcs_feature = fields.Boolean(
|
||||
string="TCS",
|
||||
compute="_compute_l10n_in_parent_based_features",
|
||||
inverse="_inverse_l10n_in_tcs_feature",
|
||||
recursive=True,
|
||||
store=True,
|
||||
)
|
||||
l10n_in_withholding_account_id = fields.Many2one(
|
||||
comodel_name='account.account',
|
||||
string="TDS Account",
|
||||
check_company=True,
|
||||
)
|
||||
l10n_in_withholding_journal_id = fields.Many2one(
|
||||
comodel_name='account.journal',
|
||||
string="TDS Journal",
|
||||
check_company=True,
|
||||
)
|
||||
|
||||
# GST settings
|
||||
l10n_in_is_gst_registered = fields.Boolean(
|
||||
string="Registered Under GST",
|
||||
compute="_compute_l10n_in_parent_based_features",
|
||||
inverse="_inverse_l10n_in_is_gst_registered",
|
||||
recursive=True,
|
||||
store=True,
|
||||
)
|
||||
l10n_in_gstin_status_feature = fields.Boolean(string="Check GST Number Status")
|
||||
|
||||
def _inverse_l10n_in_tds_feature(self):
|
||||
for company in self:
|
||||
self._activate_l10n_in_taxes(['tds_group'], company, company.l10n_in_tds_feature)
|
||||
|
||||
def _inverse_l10n_in_tcs_feature(self):
|
||||
for company in self:
|
||||
self._activate_l10n_in_taxes(['tcs_group'], company, company.l10n_in_tcs_feature)
|
||||
|
||||
def _inverse_l10n_in_is_gst_registered(self):
|
||||
for company in self:
|
||||
gst_group_refs = [
|
||||
'sgst_group',
|
||||
'cgst_group',
|
||||
'igst_group',
|
||||
'cess_group',
|
||||
'gst_group',
|
||||
'exempt_group',
|
||||
'nil_rated_group',
|
||||
'non_gst_supplies_group',
|
||||
]
|
||||
if company.l10n_in_is_gst_registered:
|
||||
self._activate_l10n_in_taxes(gst_group_refs, company, True)
|
||||
# Set sale and purchase tax accounts when user registered under GST.
|
||||
company.account_sale_tax_id = self.env['account.chart.template'].with_company(company).ref('sgst_sale_5', raise_if_not_found=False)
|
||||
company.account_purchase_tax_id = self.env['account.chart.template'].with_company(company).ref('sgst_purchase_5', raise_if_not_found=False)
|
||||
else:
|
||||
self._activate_l10n_in_taxes(gst_group_refs, company, False)
|
||||
company.account_sale_tax_id = False
|
||||
company.account_purchase_tax_id = False
|
||||
|
||||
@api.depends('parent_id.l10n_in_tds_feature', 'parent_id.l10n_in_tcs_feature', 'parent_id.l10n_in_is_gst_registered')
|
||||
def _compute_l10n_in_parent_based_features(self):
|
||||
for company in self:
|
||||
if company.parent_id:
|
||||
company.l10n_in_tds_feature = company.parent_id.l10n_in_tds_feature
|
||||
company.l10n_in_tcs_feature = company.parent_id.l10n_in_tcs_feature
|
||||
company.l10n_in_is_gst_registered = company.parent_id.l10n_in_is_gst_registered
|
||||
|
||||
def _activate_l10n_in_taxes(self, group_refs, company, active=True):
|
||||
tax_group_ids = [
|
||||
tax_group.id
|
||||
for group_ref in group_refs
|
||||
if (tax_group := self.env['account.chart.template'].with_company(company).ref(group_ref, raise_if_not_found=False))
|
||||
]
|
||||
|
||||
if tax_group_ids:
|
||||
taxes = self.env['account.tax'].with_company(company).with_context(active_test=False).search([
|
||||
('tax_group_id', 'in', tax_group_ids),
|
||||
('active', '!=', active)
|
||||
])
|
||||
taxes.write({'active': active})
|
||||
|
||||
@api.depends('vat')
|
||||
def _compute_l10n_in_hsn_code_digit(self):
|
||||
for record in self:
|
||||
if record.country_code == "IN" and record.vat:
|
||||
record.l10n_in_hsn_code_digit = "4"
|
||||
else:
|
||||
record.l10n_in_hsn_code_digit = False
|
||||
|
||||
@api.onchange('vat')
|
||||
def onchange_vat(self):
|
||||
self.partner_id.onchange_vat()
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
res = super().create(vals_list)
|
||||
# Update Fiscal Positions for new branch
|
||||
res._update_l10n_in_fiscal_position()
|
||||
return res
|
||||
|
||||
def write(self, vals):
|
||||
res = super().write(vals)
|
||||
if vals.get('vat'):
|
||||
# Enable GST(l10n_in_is_gst_registered) when a valid GSTIN(vat) is applied.
|
||||
self._update_l10n_in_is_gst_registered()
|
||||
if (vals.get('state_id') or vals.get('country_id')) and not self.env.context.get('delay_account_group_sync'):
|
||||
# Update Fiscal Positions for companies setting up state for the first time
|
||||
self._update_l10n_in_fiscal_position()
|
||||
return res
|
||||
|
||||
def _update_l10n_in_fiscal_position(self):
|
||||
companies_need_update_fp = self.filtered(lambda c: c.parent_ids[0].chart_template == 'in')
|
||||
for company in companies_need_update_fp:
|
||||
ChartTemplate = self.env['account.chart.template'].with_company(company)
|
||||
fiscal_position_data = ChartTemplate._get_in_account_fiscal_position()
|
||||
for values in fiscal_position_data.values():
|
||||
values['tax_ids'] = [Command.set([
|
||||
xml_id
|
||||
for xml_id in values['tax_ids'][0][2]
|
||||
if ChartTemplate.ref(xml_id, raise_if_not_found=False)
|
||||
])]
|
||||
ChartTemplate._load_data({'account.fiscal.position': fiscal_position_data})
|
||||
|
||||
def _update_l10n_in_is_gst_registered(self):
|
||||
for company in self:
|
||||
if company.country_code == "IN" and company.vat:
|
||||
company.l10n_in_is_gst_registered = company.partner_id.check_vat_in(company.vat)
|
||||
|
||||
def action_update_state_as_per_gstin(self):
|
||||
self.ensure_one()
|
||||
self.partner_id.action_update_state_as_per_gstin()
|
||||
|
||||
def _check_tax_return_configuration(self):
|
||||
"""
|
||||
Check if the company is properly configured for tax returns.
|
||||
:raises RedirectWarning: if something is wrong configured.
|
||||
"""
|
||||
|
||||
if self.country_code != 'IN':
|
||||
return super()._check_tax_return_configuration()
|
||||
|
||||
is_l10n_in_reports_installed = 'l10n_in_reports' in self.env['ir.module.module']._installed()
|
||||
if not is_l10n_in_reports_installed:
|
||||
msg = _("First enable GST e-Filing feature from configuration for company %s.", (self.name))
|
||||
action = self.env.ref("account.action_account_config")
|
||||
raise RedirectWarning(msg, action.id, _('Go to configuration'))
|
||||
30
odoo-bringout-oca-ocb-l10n_in/l10n_in/models/iap_account.py
Normal file
30
odoo-bringout-oca-ocb-l10n_in/l10n_in/models/iap_account.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
from odoo import api, models
|
||||
from odoo.addons.iap import jsonrpc
|
||||
|
||||
DEFAULT_IAP_ENDPOINT = "https://l10n-in-edi.api.odoo.com"
|
||||
DEFAULT_IAP_TEST_ENDPOINT = "https://l10n-in-edi-demo.api.odoo.com"
|
||||
IAP_SERVICE_NAME = 'l10n_in_edi'
|
||||
TEST_GST_NUMBER = '24FANCY1234AAZA'
|
||||
|
||||
|
||||
class IapAccount(models.Model):
|
||||
_inherit = 'iap.account'
|
||||
|
||||
@api.model
|
||||
def _l10n_in_connect_to_server(self, is_production, params, url_path, config_parameter, timeout=25):
|
||||
IrConfigParam = self.env['ir.config_parameter'].sudo()
|
||||
user_token = self.get(IAP_SERVICE_NAME)
|
||||
params.update({
|
||||
"dbuuid": IrConfigParam.get_param("database.uuid"),
|
||||
"account_token": user_token.sudo().account_token,
|
||||
})
|
||||
gsp_provider = IrConfigParam.get_param("l10n_in.gsp_provider")
|
||||
if gsp_provider:
|
||||
params.update({"gsp_provider": gsp_provider})
|
||||
if params.get('gstin') == TEST_GST_NUMBER:
|
||||
default_endpoint = DEFAULT_IAP_TEST_ENDPOINT
|
||||
else:
|
||||
default_endpoint = DEFAULT_IAP_ENDPOINT if is_production else DEFAULT_IAP_TEST_ENDPOINT
|
||||
endpoint = IrConfigParam.get_param(config_parameter, default_endpoint)
|
||||
url = "%s%s" % (endpoint, url_path)
|
||||
return jsonrpc(url, params=params, timeout=timeout)
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import base64
|
||||
|
||||
from stdnum.in_ import pan
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class L10nInPanEntity(models.Model):
|
||||
_name = 'l10n_in.pan.entity'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = 'Indian PAN Entity'
|
||||
|
||||
name = fields.Char(string="PAN", tracking=1, required=True)
|
||||
type = fields.Selection([
|
||||
('a', 'Association of Persons'),
|
||||
('b', 'Body of Individuals'),
|
||||
('c', 'Company'),
|
||||
('f', 'Firms'),
|
||||
('g', 'Government'),
|
||||
('h', 'Hindu Undivided Family'),
|
||||
('j', 'Artificial Judicial Person'),
|
||||
('l', 'Local Authority'),
|
||||
('p', 'Individual'),
|
||||
('t', 'Association of Persons for a Trust'),
|
||||
('k', 'Krish (Trust Krish)'),
|
||||
], compute='_compute_type', readonly=True, store=True)
|
||||
partner_ids = fields.One2many(
|
||||
comodel_name='res.partner',
|
||||
inverse_name='l10n_in_pan_entity_id',
|
||||
string="Partners",
|
||||
domain="[('l10n_in_pan_entity_id', '=', False), '|', ('vat', '=', False), ('vat', 'like', name)]"
|
||||
)
|
||||
tds_deduction = fields.Selection([
|
||||
('normal', 'Normal'),
|
||||
('lower', 'Lower'),
|
||||
('higher', 'Higher'),
|
||||
('no', 'No'),
|
||||
], string="TDS Deduction", default='normal', tracking=2)
|
||||
tds_certificate = fields.Binary(string="TDS Certificate", copy=False)
|
||||
tds_certificate_filename = fields.Char(string="TDS Certificate Filename", copy=False)
|
||||
|
||||
# MSME/Udyam Registration details
|
||||
msme_type = fields.Selection([
|
||||
("micro", "Micro"),
|
||||
("small", "Small"),
|
||||
("medium", "Medium")
|
||||
], string="MSME/Udyam Registration Type", copy=False)
|
||||
msme_number = fields.Char(string="MSME/Udyam Registration Number", copy=False)
|
||||
|
||||
_name_uniq = models.Constraint(
|
||||
'unique (name)',
|
||||
'A PAN Entity with same PAN Number already exists.',
|
||||
)
|
||||
|
||||
@api.constrains('name')
|
||||
def _check_pan_name(self):
|
||||
if 'import_file' in self.env.context:
|
||||
return
|
||||
for record in self:
|
||||
if record.name and not pan.is_valid(record.name):
|
||||
raise ValidationError(_("The entered PAN %s seems invalid. Please enter a valid PAN.", record.name))
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
records = super().create(vals_list)
|
||||
for record in records:
|
||||
record.name = record.name.upper()
|
||||
return records
|
||||
|
||||
def write(self, vals):
|
||||
if vals.get('name'):
|
||||
vals['name'] = vals['name'].upper()
|
||||
res = super().write(vals)
|
||||
if vals.get('tds_certificate'):
|
||||
for rec in self:
|
||||
rec.message_post(
|
||||
body=_("TDS Certificate Added"),
|
||||
message_type='notification',
|
||||
subtype_xmlid='mail.mt_note',
|
||||
attachments=[(rec.tds_certificate_filename, base64.b64decode(vals['tds_certificate']))]
|
||||
)
|
||||
return res
|
||||
|
||||
@api.depends('name')
|
||||
def _compute_type(self):
|
||||
for record in self:
|
||||
if record.name:
|
||||
if pan.is_valid(record.name):
|
||||
record.type = record.name[3].lower()
|
||||
else:
|
||||
record.type = False
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
from odoo import models
|
||||
|
||||
|
||||
class AccountReport(models.Model):
|
||||
_inherit = 'account.report'
|
||||
|
||||
def _init_options_buttons(self, options, previous_options):
|
||||
super()._init_options_buttons(options, previous_options)
|
||||
company = self.env.company
|
||||
generic_report_id = self.env.ref('account.generic_tax_report').id
|
||||
|
||||
# Remove 'Returns' button from generic report for indian company
|
||||
if company.country_id.code == 'IN' and self.id == generic_report_id and not self.root_report_id:
|
||||
options['buttons'] = [
|
||||
button for button in options['buttons']
|
||||
if button.get('action') != 'action_open_returns'
|
||||
]
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class L10n_InSectionAlert(models.Model):
|
||||
_name = 'l10n_in.section.alert'
|
||||
_description = "indian section alert"
|
||||
|
||||
name = fields.Char("Section Name")
|
||||
tax_source_type = fields.Selection([
|
||||
('tds', 'TDS'),
|
||||
('tcs', 'TCS'),
|
||||
], string="Tax Source Type")
|
||||
consider_amount = fields.Selection([
|
||||
('untaxed_amount', 'Untaxed Amount'),
|
||||
('total_amount', 'Total Amount'),
|
||||
], string="Consider", default='untaxed_amount', required=True)
|
||||
is_per_transaction_limit = fields.Boolean("Per Transaction")
|
||||
per_transaction_limit = fields.Float("Per Transaction limit")
|
||||
is_aggregate_limit = fields.Boolean("Aggregate")
|
||||
aggregate_limit = fields.Float("Aggregate limit")
|
||||
aggregate_period = fields.Selection([
|
||||
('monthly', 'Monthly'),
|
||||
('fiscal_yearly', 'Financial Yearly'),
|
||||
], string="Aggregate Period", default='fiscal_yearly')
|
||||
l10n_in_section_tax_ids = fields.One2many("account.tax", "l10n_in_section_id", string="Taxes")
|
||||
tax_report_line_id = fields.Many2one(string="Tax Report Line", comodel_name='account.report.line')
|
||||
|
||||
_per_transaction_limit = models.Constraint(
|
||||
'CHECK(per_transaction_limit >= 0)',
|
||||
'Per transaction limit must be positive',
|
||||
)
|
||||
_aggregate_limit = models.Constraint(
|
||||
'CHECK(aggregate_limit >= 0)',
|
||||
'Aggregate limit must be positive',
|
||||
)
|
||||
|
||||
@api.depends('tax_source_type')
|
||||
def _compute_display_name(self):
|
||||
for record in self:
|
||||
record.display_name = f"{record.tax_source_type.upper()} {record.name or ''}" if record.tax_source_type else f"{record.name or ''}"
|
||||
|
||||
def _get_warning_message(self):
|
||||
warning = ", ".join(self.mapped('name'))
|
||||
section_type = next(iter(set(self.mapped('tax_source_type')))).upper()
|
||||
action = _('collect') if section_type == 'TCS' else _('deduct')
|
||||
return _("It's advisable to %(action)s %(section_type)s u/s %(warning)s on this transaction.",
|
||||
action=action,
|
||||
section_type=section_type,
|
||||
warning=warning
|
||||
)
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import fields, api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class Message(models.Model):
|
||||
_inherit = 'mail.message'
|
||||
|
||||
l10n_in_audit_log_preview = fields.Html(string="Description", compute="_compute_l10n_in_audit_log_preview")
|
||||
l10n_in_audit_log_account_move_id = fields.Many2one('account.move', string="Journal Entry", compute="_compute_l10n_in_audit_log_document_name", search="_search_l10n_in_audit_log_document_name")
|
||||
|
||||
@api.depends('body', 'subject', 'tracking_value_ids', 'subtype_id')
|
||||
def _compute_l10n_in_audit_log_preview(self):
|
||||
for message in self:
|
||||
title = message.subject or message.preview
|
||||
tracking_value_ids = message.sudo().tracking_value_ids
|
||||
if not title and tracking_value_ids:
|
||||
title = _("Updated")
|
||||
elif not title and message.subtype_id and not message.subtype_id.internal:
|
||||
title = message.subtype_id.display_name
|
||||
audit_log_preview = Markup("<div>%s</div>") % (title)
|
||||
for value in tracking_value_ids:
|
||||
audit_log_preview += Markup(
|
||||
"<li>%(old_value)s <i class='o_TrackingValue_separator fa fa-long-arrow-right mx-1 text-600' title='%(title)s' role='img' aria-label='%(title)s'></i>%(new_value)s (%(field)s)</li>"
|
||||
) % {
|
||||
'old_value': value._get_old_display_value()[0] or _("None"),
|
||||
'new_value': value._get_new_display_value()[0] or _("None"),
|
||||
'title': _("Changed"),
|
||||
'field': value.field.field_description,
|
||||
}
|
||||
message.l10n_in_audit_log_preview = audit_log_preview
|
||||
|
||||
@api.depends('model', 'res_id')
|
||||
def _compute_l10n_in_audit_log_document_name(self):
|
||||
messages_of_account_move = self.filtered(lambda m: m.model == 'account.move' and m.res_id)
|
||||
(self - messages_of_account_move).l10n_in_audit_log_account_move_id = False
|
||||
moves = self.env['account.move'].search([('id', 'in', messages_of_account_move.mapped('res_id'))])
|
||||
moves_by_id = {m.id: m for m in moves}
|
||||
for message in messages_of_account_move:
|
||||
message.l10n_in_audit_log_account_move_id = moves_by_id.get(message.res_id, False)
|
||||
|
||||
def _search_l10n_in_audit_log_document_name(self, operator, value):
|
||||
is_set = False
|
||||
if operator == '!=' and isinstance(value, bool):
|
||||
is_set = True
|
||||
elif operator not in ['=', 'ilike'] or not isinstance(value, str):
|
||||
raise UserError(_('Operation not supported'))
|
||||
move_domain = [('company_id.account_fiscal_country_id.code', '=', 'IN')]
|
||||
if not is_set:
|
||||
move_domain += [('name', operator, value)]
|
||||
move_query = self.env['account.move']._search(move_domain)
|
||||
return [('model', '=', 'account.move'), ('res_id', 'in', move_query)]
|
||||
|
|
@ -1,10 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class L10nInPortCode(models.Model):
|
||||
class L10n_InPortCode(models.Model):
|
||||
"""Port code must be mentioned in export and import of goods under GST."""
|
||||
_name = 'l10n_in.port.code'
|
||||
_description = "Indian port code"
|
||||
|
|
@ -14,6 +11,7 @@ class L10nInPortCode(models.Model):
|
|||
name = fields.Char(string="Port", required=True)
|
||||
state_id = fields.Many2one('res.country.state', string="State")
|
||||
|
||||
_sql_constraints = [
|
||||
('code_uniq', 'unique (code)', 'The Port Code must be unique!')
|
||||
]
|
||||
_code_uniq = models.Constraint(
|
||||
'unique (code)',
|
||||
'The Port Code must be unique!',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,41 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, fields
|
||||
import re
|
||||
from odoo import _, api, models, fields
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = 'product.template'
|
||||
|
||||
l10n_in_hsn_code = fields.Char(string="HSN/SAC Code", help="Harmonized System Nomenclature/Services Accounting Code")
|
||||
l10n_in_hsn_description = fields.Char(string="HSN/SAC Description", help="HSN/SAC description is required if HSN/SAC code is not provided.")
|
||||
l10n_in_hsn_warning = fields.Text(string="HSC/SAC warning", compute="_compute_l10n_in_hsn_warning")
|
||||
l10n_in_is_gst_registered_enabled = fields.Boolean(compute="_compute_l10n_in_is_gst_registered_enabled")
|
||||
|
||||
@api.depends('company_id.l10n_in_is_gst_registered')
|
||||
@api.depends_context('allowed_company_ids')
|
||||
def _compute_l10n_in_is_gst_registered_enabled(self):
|
||||
for record in self:
|
||||
allowed_companies = record.company_id or self.env.companies
|
||||
record.l10n_in_is_gst_registered_enabled = any(
|
||||
company.l10n_in_is_gst_registered
|
||||
for company in allowed_companies
|
||||
)
|
||||
|
||||
@api.depends('sale_ok', 'l10n_in_hsn_code')
|
||||
def _compute_l10n_in_hsn_warning(self):
|
||||
digit_suffixes = {
|
||||
'4': _("either 4, 6 or 8"),
|
||||
'6': _("either 6 or 8"),
|
||||
'8': _("8")
|
||||
}
|
||||
active_hsn_code_digit_len = max(
|
||||
int(company.l10n_in_hsn_code_digit)
|
||||
for company in self.env.companies
|
||||
)
|
||||
for record in self:
|
||||
check_hsn = record.sale_ok and record.l10n_in_hsn_code and active_hsn_code_digit_len
|
||||
if check_hsn and (not re.match(r'^\d{4}$|^\d{6}$|^\d{8}$', record.l10n_in_hsn_code) or len(record.l10n_in_hsn_code) < active_hsn_code_digit_len):
|
||||
record.l10n_in_hsn_warning = _(
|
||||
"HSN code field must consist solely of digits and be %s in length.",
|
||||
digit_suffixes.get(str(active_hsn_code_digit_len))
|
||||
)
|
||||
continue
|
||||
record.l10n_in_hsn_warning = False
|
||||
|
|
|
|||
|
|
@ -1,10 +1,190 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import _, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError, RedirectWarning
|
||||
from odoo.tools.sql import column_exists, create_column
|
||||
from odoo.tools import SQL
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.addons.l10n_in.models.iap_account import IAP_SERVICE_NAME
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
group_l10n_in_reseller = fields.Boolean(implied_group='l10n_in.group_l10n_in_reseller', string="Manage Reseller(E-Commerce)")
|
||||
group_l10n_in_reseller = fields.Boolean(
|
||||
implied_group='l10n_in.group_l10n_in_reseller',
|
||||
string="Manage Reseller(E-Commerce)"
|
||||
)
|
||||
l10n_in_edi_production_env = fields.Boolean(
|
||||
string="Indian Production Environment",
|
||||
related="company_id.l10n_in_edi_production_env",
|
||||
readonly=False
|
||||
)
|
||||
l10n_in_gsp = fields.Selection(selection=[
|
||||
('bvm', 'BVM IT Consulting'),
|
||||
('tera', 'Tera Software (Deprecated)'),
|
||||
], string="GSP",
|
||||
inverse="_set_l10n_in_gsp", # use an inverse method to invalidate existing tokens if the GSP is changed
|
||||
store=False,
|
||||
help="Select the GST Suvidha Provider (GSP) you want to use for GST services.",
|
||||
)
|
||||
l10n_in_hsn_code_digit = fields.Selection(
|
||||
related='company_id.l10n_in_hsn_code_digit',
|
||||
readonly=False
|
||||
)
|
||||
|
||||
# TDS/TCS settings
|
||||
l10n_in_tds_feature = fields.Boolean(
|
||||
related='company_id.l10n_in_tds_feature',
|
||||
readonly=False
|
||||
)
|
||||
l10n_in_tcs_feature = fields.Boolean(
|
||||
related='company_id.l10n_in_tcs_feature',
|
||||
readonly=False
|
||||
)
|
||||
l10n_in_withholding_account_id = fields.Many2one(
|
||||
related='company_id.l10n_in_withholding_account_id',
|
||||
readonly=False
|
||||
)
|
||||
l10n_in_withholding_journal_id = fields.Many2one(
|
||||
related='company_id.l10n_in_withholding_journal_id',
|
||||
readonly=False
|
||||
)
|
||||
l10n_in_tan = fields.Char(
|
||||
related='company_id.l10n_in_tan',
|
||||
readonly=False
|
||||
)
|
||||
|
||||
# GST settings
|
||||
l10n_in_is_gst_registered = fields.Boolean(
|
||||
related='company_id.l10n_in_is_gst_registered',
|
||||
readonly=False
|
||||
)
|
||||
l10n_in_gstin = fields.Char(
|
||||
string="GST Number",
|
||||
related='company_id.vat',
|
||||
readonly=False
|
||||
)
|
||||
l10n_in_gstin_status_feature = fields.Boolean(
|
||||
related='company_id.l10n_in_gstin_status_feature',
|
||||
readonly=False
|
||||
)
|
||||
l10n_in_gst_efiling_feature = fields.Boolean(string="GST E-Filing & Matching Feature")
|
||||
l10n_in_fetch_vendor_edi_feature = fields.Boolean(string="Fetch Vendor E-Invoiced Document")
|
||||
l10n_in_enet_vendor_batch_payment_feature = fields.Boolean(string="ENet Vendor Batch Payment")
|
||||
|
||||
module_l10n_in_reports = fields.Boolean("GST E-Filing & Matching")
|
||||
module_l10n_in_edi = fields.Boolean("Indian Electronic Invoicing")
|
||||
module_l10n_in_ewaybill = fields.Boolean("Indian Electronic Waybill")
|
||||
|
||||
def set_values(self):
|
||||
super().set_values()
|
||||
if self.country_code == 'IN':
|
||||
if (
|
||||
not self.module_l10n_in_reports
|
||||
and (
|
||||
self.l10n_in_fetch_vendor_edi_feature
|
||||
or self.l10n_in_gst_efiling_feature
|
||||
or self.l10n_in_enet_vendor_batch_payment_feature
|
||||
)
|
||||
):
|
||||
self.module_l10n_in_reports = True
|
||||
for l10n_in_feature in (
|
||||
"l10n_in_fetch_vendor_edi_feature",
|
||||
"l10n_in_gst_efiling_feature",
|
||||
"l10n_in_enet_vendor_batch_payment_feature",
|
||||
):
|
||||
if self[l10n_in_feature]:
|
||||
self._update_l10n_in_feature(l10n_in_feature)
|
||||
if self.module_l10n_in_edi:
|
||||
self._update_l10n_in_feature("l10n_in_edi_feature")
|
||||
if self.module_l10n_in_ewaybill:
|
||||
self._update_l10n_in_feature("l10n_in_ewaybill_feature")
|
||||
|
||||
def _update_l10n_in_feature(self, column):
|
||||
""" This way, after installing the module, the field will already be set for the active company. """
|
||||
if not column_exists(self.env.cr, "res_company", column):
|
||||
create_column(self.env.cr, "res_company", column, "boolean")
|
||||
self.env.cr.execute(SQL(
|
||||
f"""
|
||||
UPDATE res_company
|
||||
SET {column} = true
|
||||
WHERE id = {self.env.company.id}
|
||||
"""
|
||||
))
|
||||
|
||||
def l10n_in_edi_buy_iap(self):
|
||||
if (
|
||||
not self.l10n_in_edi_production_env
|
||||
or not (
|
||||
self.module_l10n_in_edi
|
||||
or self.module_l10n_in_ewaybill
|
||||
or self.l10n_in_gstin_status_feature
|
||||
or self.l10n_in_gst_efiling_feature
|
||||
)
|
||||
):
|
||||
raise ValidationError(_(
|
||||
"Please ensure that at least one Indian service and production environment is enabled,"
|
||||
" and save the configuration to proceed with purchasing credits."
|
||||
))
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': self.env["iap.account"].get_credits_url(service_name=IAP_SERVICE_NAME),
|
||||
'target': '_new'
|
||||
}
|
||||
|
||||
def _l10n_in_check_gst_number(self):
|
||||
company = self.company_id
|
||||
if not company.partner_id.check_vat_in(company.vat):
|
||||
action = {
|
||||
'view_mode': 'form',
|
||||
'res_model': 'res.company',
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_id': company.id,
|
||||
'views': [[self.env.ref('base.view_company_form').id, 'form']],
|
||||
}
|
||||
raise RedirectWarning(_("Please set a valid GST number on company."), action, _("Go to Company"))
|
||||
|
||||
def reload_template(self):
|
||||
super().reload_template()
|
||||
if self.country_code == 'IN':
|
||||
branch_companies = self.company_id.child_ids
|
||||
if branch_companies:
|
||||
branch_companies._update_l10n_in_fiscal_position()
|
||||
|
||||
def _l10n_in_is_first_time_setup(self):
|
||||
"""
|
||||
Check if at least one company for India has been configured with the localization settings.
|
||||
If not, it means it's the first time setup.
|
||||
"""
|
||||
all_validity_fields = ['l10n_in_gstr_gst_token_validity', 'l10n_in_edi_token_validity', 'l10n_in_ewaybill_auth_validity']
|
||||
validity_fields = (field_name for field_name in self.company_id._fields if field_name in all_validity_fields)
|
||||
if validity_fields:
|
||||
validity_fields_domain = fields.Domain.OR([[(field_name, '!=', False)] for field_name in validity_fields])
|
||||
configured_company_count = self.env['res.company'].sudo().search_count([
|
||||
('account_fiscal_country_id.code', '=', 'IN'),
|
||||
*validity_fields_domain
|
||||
])
|
||||
return not configured_company_count
|
||||
return True
|
||||
|
||||
def get_values(self):
|
||||
res = super().get_values()
|
||||
res['l10n_in_gsp'] = self.env['ir.config_parameter'].sudo().get_param('l10n_in.gsp_provider')
|
||||
if not res['l10n_in_gsp']:
|
||||
if self._l10n_in_is_first_time_setup():
|
||||
# Default to BVM for new databases setting up India localization for the first time
|
||||
res['l10n_in_gsp'] = 'bvm'
|
||||
else:
|
||||
res['l10n_in_gsp'] = 'tera'
|
||||
return res
|
||||
|
||||
def _l10n_in_gsp_provider_changed(self):
|
||||
""" Hook to be overridden in other modules to handle GSP provider change. """
|
||||
self.ensure_one()
|
||||
self.env['ir.config_parameter'].sudo().set_param('l10n_in.gsp_provider', self.l10n_in_gsp)
|
||||
|
||||
def _set_l10n_in_gsp(self):
|
||||
gsp_before = self.env['ir.config_parameter'].sudo().get_param('l10n_in.gsp_provider')
|
||||
for config in self:
|
||||
if gsp_before != config.l10n_in_gsp:
|
||||
config._l10n_in_gsp_provider_changed()
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class CountryState(models.Model):
|
||||
class ResCountryState(models.Model):
|
||||
_inherit = 'res.country.state'
|
||||
|
||||
l10n_in_tin = fields.Char('TIN Number', size=2, help="TIN number-first two digits")
|
||||
l10n_in_tin = fields.Char("TIN Number", size=2, help="TIN number-first two digits")
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import logging
|
||||
import re
|
||||
from stdnum.in_ import pan
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, AccessError, ValidationError
|
||||
from odoo.addons.l10n_in.models.iap_account import IAP_SERVICE_NAME
|
||||
from odoo.tools.misc import clean_context
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
TEST_GST_NUMBER = "36AABCT1332L011"
|
||||
TEST_GST_NUMBER_BVM = "29AAGCB1286Q000"
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
|
@ -19,33 +27,206 @@ class ResPartner(models.Model):
|
|||
('uin_holders', 'UIN Holders'),
|
||||
], string="GST Treatment")
|
||||
|
||||
@api.onchange('company_type')
|
||||
def onchange_company_type(self):
|
||||
res = super().onchange_company_type()
|
||||
if self.country_id and self.country_id.code == 'IN':
|
||||
self.l10n_in_gst_treatment = (self.company_type == 'company') and 'regular' or 'consumer'
|
||||
l10n_in_pan_entity_id = fields.Many2one(
|
||||
comodel_name='l10n_in.pan.entity',
|
||||
string="PAN",
|
||||
ondelete='restrict',
|
||||
help="PAN enables the department to link all transactions of the person with the department.\n"
|
||||
"These transactions include taxpayments, TDS/TCS credits, returns of income/wealth/gift/FBT,"
|
||||
" specified transactions, correspondence, and so on.\n"
|
||||
"Thus, PAN acts as an identifier for the person with the tax department."
|
||||
)
|
||||
l10n_in_tan = fields.Char("TAN")
|
||||
|
||||
display_pan_warning = fields.Boolean(string="Display pan warning", compute="_compute_display_pan_warning")
|
||||
l10n_in_gst_state_warning = fields.Char(compute="_compute_l10n_in_gst_state_warning")
|
||||
l10n_in_is_gst_registered_enabled = fields.Boolean(compute="_compute_l10n_in_gst_registered_and_status")
|
||||
|
||||
# gstin_status related field
|
||||
l10n_in_gstin_verified_status = fields.Boolean(string="GST Status", tracking=True)
|
||||
l10n_in_gstin_verified_date = fields.Date(string="GSTIN Verified Date", tracking=True)
|
||||
l10n_in_gstin_status_feature_enabled = fields.Boolean(compute="_compute_l10n_in_gst_registered_and_status")
|
||||
|
||||
@api.depends('vat', 'state_id', 'country_id', 'fiscal_country_codes')
|
||||
def _compute_l10n_in_gst_state_warning(self):
|
||||
for partner in self:
|
||||
if (
|
||||
"IN" in partner.fiscal_country_codes
|
||||
and partner.check_vat_in(partner.vat)
|
||||
):
|
||||
if partner.vat[:2] == "99":
|
||||
partner.l10n_in_gst_state_warning = _(
|
||||
"As per GSTN the country should be other than India, so it's recommended to"
|
||||
)
|
||||
else:
|
||||
state_id = self.env['res.country.state'].search([('l10n_in_tin', '=', partner.vat[:2])], limit=1)
|
||||
if state_id and state_id != partner.state_id:
|
||||
partner.l10n_in_gst_state_warning = _(
|
||||
"As per GSTN the state should be %s, so it's recommended to", state_id.name
|
||||
)
|
||||
else:
|
||||
partner.l10n_in_gst_state_warning = False
|
||||
else:
|
||||
partner.l10n_in_gst_state_warning = False
|
||||
|
||||
@api.depends('l10n_in_pan_entity_id')
|
||||
def _compute_display_pan_warning(self):
|
||||
for partner in self:
|
||||
partner.display_pan_warning = partner.vat and partner.l10n_in_pan_entity_id and partner.l10n_in_pan_entity_id.name != partner.vat[2:12]
|
||||
|
||||
@api.depends('company_id.l10n_in_is_gst_registered', 'company_id.l10n_in_gstin_status_feature')
|
||||
def _compute_l10n_in_gst_registered_and_status(self):
|
||||
for record in self:
|
||||
company = record.company_id or self.env.company
|
||||
record.l10n_in_is_gst_registered_enabled = company.l10n_in_is_gst_registered
|
||||
record.l10n_in_gstin_status_feature_enabled = company.l10n_in_gstin_status_feature
|
||||
|
||||
@api.onchange('vat')
|
||||
def _onchange_l10n_in_gst_status(self):
|
||||
"""
|
||||
Reset GST Status Whenever the `vat` of partner changes
|
||||
"""
|
||||
for partner in self:
|
||||
if partner.country_code == 'IN' and (partner.l10n_in_gstin_verified_status or partner.l10n_in_gstin_verified_date):
|
||||
partner.l10n_in_gstin_verified_status = False
|
||||
partner.l10n_in_gstin_verified_date = False
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
res = super().create(vals_list)
|
||||
if 'import_file' in self.env.context:
|
||||
return res
|
||||
for partner in res.filtered(lambda p: p.country_code == 'IN' and p.vat and p.check_vat_in(p.vat)):
|
||||
partner._set_l10n_in_pan_tan_from_vat()
|
||||
return res
|
||||
|
||||
@api.onchange('country_id')
|
||||
def _onchange_country_id(self):
|
||||
res = super()._onchange_country_id()
|
||||
if self.country_id and self.country_id.code != 'IN':
|
||||
self.l10n_in_gst_treatment = 'overseas'
|
||||
elif self.country_id and self.country_id.code == 'IN':
|
||||
self.l10n_in_gst_treatment = (self.company_type == 'company') and 'regular' or 'consumer'
|
||||
def write(self, vals):
|
||||
res = super().write(vals)
|
||||
if 'import_file' in self.env.context:
|
||||
return res
|
||||
if vals.get('vat') or vals.get('country_id'):
|
||||
for partner in self.filtered(lambda p: p.country_code == 'IN' and p.vat and p.check_vat_in(p.vat)):
|
||||
partner._set_l10n_in_pan_tan_from_vat()
|
||||
return res
|
||||
|
||||
def _set_l10n_in_pan_tan_from_vat(self):
|
||||
self.ensure_one()
|
||||
identifier = self.vat[2:12].upper()
|
||||
if pan.is_valid(identifier):
|
||||
self.l10n_in_pan_entity_id = self._l10n_in_search_create_pan_entity_from_vat(self.vat).id
|
||||
elif re.match(r'^[A-Z]{4}[0-9]{5}[A-Z]{1}$', identifier):
|
||||
self.l10n_in_tan = identifier
|
||||
|
||||
def _l10n_in_search_create_pan_entity_from_vat(self, vat):
|
||||
pan_number = vat[2:12].upper()
|
||||
pan_entity = self.env['l10n_in.pan.entity'].search([('name', '=', pan_number)], limit=1)
|
||||
if not pan_entity:
|
||||
context = clean_context(self.env.context)
|
||||
pan_entity = self.env['l10n_in.pan.entity'].with_context(context).create({'name': pan_number})
|
||||
return pan_entity
|
||||
|
||||
def action_l10n_in_verify_gstin_status(self):
|
||||
self.ensure_one()
|
||||
self.check_access('write')
|
||||
if self.env.company.sudo().account_fiscal_country_id.code != 'IN':
|
||||
raise UserError(_('You must be logged in an Indian company to use this feature'))
|
||||
if not self.vat:
|
||||
raise ValidationError(_("Please enter the GSTIN"))
|
||||
if not self.env.company.l10n_in_gstin_status_feature:
|
||||
raise ValidationError(_("This feature is not activated. Go to Settings to activate this feature."))
|
||||
is_production = self.env.company.sudo().l10n_in_edi_production_env
|
||||
params = {
|
||||
"gstin_to_search": self.vat,
|
||||
"gstin": self.env.company.vat,
|
||||
}
|
||||
try:
|
||||
response = self.env['iap.account']._l10n_in_connect_to_server(
|
||||
is_production,
|
||||
params,
|
||||
'/iap/l10n_in_reports/1/public/search',
|
||||
"l10n_in.endpoint"
|
||||
)
|
||||
except AccessError:
|
||||
raise UserError(_("Unable to connect with GST network"))
|
||||
if response.get('error') and any(e.get('code') == 'no-credit' for e in response['error']):
|
||||
return self.env["bus.bus"]._sendone(self.env.user.partner_id, "iap_notification",
|
||||
{
|
||||
"type": "no_credit",
|
||||
"title": _("Not enough credits to check GSTIN status"),
|
||||
"get_credits_url": self.env["iap.account"].get_credits_url(service_name=IAP_SERVICE_NAME),
|
||||
},
|
||||
)
|
||||
gst_status = response.get('data', {}).get('sts', "")
|
||||
if gst_status.casefold() == 'active':
|
||||
l10n_in_gstin_verified_status = True
|
||||
elif gst_status:
|
||||
l10n_in_gstin_verified_status = False
|
||||
date_from = response.get("data", {}).get("cxdt", '')
|
||||
if date_from and re.search(r'\d', date_from):
|
||||
message = _(
|
||||
"GSTIN %(vat)s is %(status)s and Effective from %(date_from)s.",
|
||||
vat=self.vat,
|
||||
status=gst_status,
|
||||
date_from=date_from,
|
||||
)
|
||||
else:
|
||||
message = _(
|
||||
"GSTIN %(vat)s is %(status)s, effective date is not available.",
|
||||
vat=self.vat,
|
||||
status=gst_status
|
||||
)
|
||||
if not is_production:
|
||||
message += _(" Warning: You are currently in a test environment. The result is a dummy.")
|
||||
self.message_post(body=message)
|
||||
else:
|
||||
_logger.info("GST status check error %s", response)
|
||||
if response.get('error') and any(e.get('code') == 'SWEB_9035' for e in response['error']):
|
||||
raise UserError(
|
||||
_("The provided GSTIN is invalid. Please check the GSTIN and try again.")
|
||||
)
|
||||
default_error_message = _(
|
||||
"Something went wrong while fetching the GST status."
|
||||
"Please Contact Support if the error persists with"
|
||||
"Response: %(response)s",
|
||||
response=response
|
||||
)
|
||||
error_messages = [
|
||||
f"[{error.get('code') or _('Unknown')}] {error.get('message') or default_error_message}"
|
||||
for error in response.get('error')
|
||||
]
|
||||
raise UserError(
|
||||
error_messages
|
||||
and '\n'.join(error_messages)
|
||||
or default_error_message
|
||||
)
|
||||
self.write({
|
||||
"l10n_in_gstin_verified_status": l10n_in_gstin_verified_status,
|
||||
"l10n_in_gstin_verified_date": fields.Date.today(),
|
||||
})
|
||||
return {
|
||||
"type": "ir.actions.client",
|
||||
"tag": "display_notification",
|
||||
"params": {
|
||||
"type": "info",
|
||||
"message": _("GSTIN Status Updated Successfully"),
|
||||
"next": {"type": "ir.actions.act_window_close"},
|
||||
},
|
||||
}
|
||||
|
||||
@api.onchange('vat')
|
||||
def onchange_vat(self):
|
||||
if self.vat and self.check_vat_in(self.vat):
|
||||
self.vat = self.vat.upper()
|
||||
state_id = self.env['res.country.state'].search([('l10n_in_tin', '=', self.vat[:2])], limit=1)
|
||||
if state_id:
|
||||
self.state_id = state_id
|
||||
pan_entity = self.env['l10n_in.pan.entity'].search([('name', '=', self.vat[2:12])], limit=1)
|
||||
if pan_entity:
|
||||
self.l10n_in_pan_entity_id = pan_entity.id
|
||||
|
||||
@api.model
|
||||
def _commercial_fields(self):
|
||||
res = super()._commercial_fields()
|
||||
return res + ['l10n_in_gst_treatment']
|
||||
return super()._commercial_fields() + ['l10n_in_gst_treatment', 'l10n_in_pan_entity_id', 'l10n_in_tan']
|
||||
|
||||
def check_vat_in(self, vat):
|
||||
"""
|
||||
|
|
@ -53,6 +234,28 @@ class ResPartner(models.Model):
|
|||
but this is not a valid number as per the regular expression
|
||||
so TEST_GST_NUMBER is considered always valid
|
||||
"""
|
||||
if vat == TEST_GST_NUMBER:
|
||||
if vat in (TEST_GST_NUMBER, TEST_GST_NUMBER_BVM):
|
||||
return True
|
||||
return super().check_vat_in(vat)
|
||||
|
||||
@api.model
|
||||
def _l10n_in_get_partner_vals_by_vat(self, vat):
|
||||
partner_data = self.enrich_by_gst(vat)
|
||||
for fname in list(partner_data.keys()):
|
||||
if fname not in self.env['res.partner']._fields:
|
||||
partner_data.pop(fname, None)
|
||||
partner_data.update({
|
||||
'country_id': partner_data.get('country_id', {}).get('id'),
|
||||
'state_id': partner_data.get('state_id', {}).get('id'),
|
||||
'company_type': 'company',
|
||||
'l10n_in_gst_treatment': partner_data.get('l10n_in_gst_treatment', 'regular'),
|
||||
})
|
||||
return partner_data
|
||||
|
||||
def action_update_state_as_per_gstin(self):
|
||||
self.ensure_one()
|
||||
if self.check_vat_in(self.vat):
|
||||
state_id = self.env['res.country.state'].search([('l10n_in_tin', '=', self.vat[:2])], limit=1)
|
||||
self.state_id = state_id
|
||||
if self.ref_company_ids:
|
||||
self.ref_company_ids._update_l10n_in_fiscal_position()
|
||||
|
|
|
|||
143
odoo-bringout-oca-ocb-l10n_in/l10n_in/models/template_in.py
Normal file
143
odoo-bringout-oca-ocb-l10n_in/l10n_in/models/template_in.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
from odoo import Command, models
|
||||
from odoo.addons.account.models.chart_template import template
|
||||
|
||||
|
||||
class AccountChartTemplate(models.AbstractModel):
|
||||
_inherit = 'account.chart.template'
|
||||
|
||||
@template('in')
|
||||
def _get_in_template_data(self):
|
||||
return {
|
||||
'property_account_receivable_id': 'p10040',
|
||||
'property_account_payable_id': 'p11211',
|
||||
'code_digits': '6',
|
||||
'display_invoice_amount_total_words': True,
|
||||
}
|
||||
|
||||
@template('in', 'res.company')
|
||||
def _get_in_res_company(self):
|
||||
return {
|
||||
self.env.company.id: {
|
||||
'account_fiscal_country_id': 'base.in',
|
||||
'bank_account_code_prefix': '1002',
|
||||
'cash_account_code_prefix': '1001',
|
||||
'transfer_account_code_prefix': '1008',
|
||||
'account_default_pos_receivable_account_id': 'p10041',
|
||||
'income_currency_exchange_account_id': 'p2013',
|
||||
'expense_currency_exchange_account_id': 'p2117',
|
||||
'account_journal_early_pay_discount_loss_account_id': 'p2132',
|
||||
'account_journal_early_pay_discount_gain_account_id': '2012',
|
||||
'fiscalyear_last_month': '3',
|
||||
'account_sale_tax_id': 'sgst_sale_5',
|
||||
'account_purchase_tax_id': 'sgst_purchase_5',
|
||||
'deferred_expense_account_id': 'p10084',
|
||||
'deferred_revenue_account_id': 'p10085',
|
||||
'expense_account_id': 'p2107',
|
||||
'income_account_id': 'p20011',
|
||||
'l10n_in_withholding_account_id': 'p100595',
|
||||
'tax_calculation_rounding_method': 'round_per_line',
|
||||
},
|
||||
}
|
||||
|
||||
@template('in', 'account.cash.rounding')
|
||||
def _get_in_account_cash_rounding(self):
|
||||
return {
|
||||
'l10n_in.cash_rounding_in_half_up': {
|
||||
'profit_account_id': 'p213202',
|
||||
'loss_account_id': 'p213201',
|
||||
}
|
||||
}
|
||||
|
||||
@template('in', 'account.fiscal.position')
|
||||
def _get_in_account_fiscal_position(self):
|
||||
_ = self.env._
|
||||
company = self.env.company
|
||||
state_ids = [Command.set(company.state_id.ids)] if company.state_id else False
|
||||
intra_state_name = company.state_id and _("Within %s", company.state_id.name) or _("Intra State")
|
||||
country_in_id = self.env.ref('base.in').id
|
||||
state_specific = {
|
||||
'fiscal_position_in_intra_state': {
|
||||
'name': intra_state_name,
|
||||
'sequence': 1,
|
||||
'auto_apply': True,
|
||||
'state_ids': state_ids,
|
||||
'tax_ids': self._get_l10n_in_fiscal_tax_vals('fiscal_position_in_intra_state'),
|
||||
'country_id': country_in_id,
|
||||
},
|
||||
'fiscal_position_in_inter_state': {
|
||||
'name': _("Inter State"),
|
||||
'sequence': 2,
|
||||
'auto_apply': True,
|
||||
'country_group_id': 'l10n_in.inter_state_group',
|
||||
'tax_ids': self._get_l10n_in_fiscal_tax_vals('fiscal_position_in_inter_state'),
|
||||
},
|
||||
}
|
||||
if company.parent_id:
|
||||
return {
|
||||
self.company_xmlid(k): v
|
||||
for k, v in state_specific.items()
|
||||
}
|
||||
return {
|
||||
**state_specific,
|
||||
'fiscal_position_in_sez': {
|
||||
'name': _("Special Economic Zone (SEZ)"),
|
||||
'sequence': 3,
|
||||
'auto_apply': True,
|
||||
'state_ids': [Command.set(self.env.ref('l10n_in.state_in_oc').ids)],
|
||||
'country_id': country_in_id,
|
||||
'note': _("SUPPLY MEANT FOR EXPORT/SUPPLY TO SEZ UNIT OR SEZ DEVELOPER FOR AUTHORISED OPERATIONS ON PAYMENT OF INTEGRATED TAX."),
|
||||
'tax_ids': self._get_l10n_in_fiscal_tax_vals('fiscal_position_in_inter_state'),
|
||||
},
|
||||
'fiscal_position_in_export_sez_in': {
|
||||
'name': _("Export"),
|
||||
'sequence': 4,
|
||||
'auto_apply': True,
|
||||
'note': _("SUPPLY MEANT FOR EXPORT/SUPPLY TO SEZ UNIT OR SEZ DEVELOPER FOR AUTHORISED OPERATIONS ON PAYMENT OF INTEGRATED TAX."),
|
||||
'tax_ids': self._get_l10n_in_fiscal_tax_vals('fiscal_position_in_export_sez_in'),
|
||||
},
|
||||
'fiscal_position_in_lut_sez_1': {
|
||||
'name': _("SEZ - LUT (WOP)"),
|
||||
'sequence': 5,
|
||||
'state_ids': [Command.set(self.env.ref('l10n_in.state_in_oc').ids)],
|
||||
'country_id': country_in_id,
|
||||
'note': _("SUPPLY MEANT FOR EXPORT/SUPPLY TO SEZ UNIT OR SEZ DEVELOPER FOR AUTHORISED OPERATIONS UNDER BOND OR LETTER OF UNDERTAKING WITHOUT PAYMENT OF INTEGRATED TAX."),
|
||||
'tax_ids': self._get_l10n_in_fiscal_tax_vals('fiscal_position_in_lut_sez_1'),
|
||||
},
|
||||
'fiscal_position_in_lut_sez': {
|
||||
'name': _("Export - LUT (WOP)"),
|
||||
'sequence': 6,
|
||||
'note': _('SUPPLY MEANT FOR EXPORT/SUPPLY TO SEZ UNIT OR SEZ DEVELOPER FOR AUTHORISED OPERATIONS UNDER BOND OR LETTER OF UNDERTAKING WITHOUT PAYMENT OF INTEGRATED TAX.'),
|
||||
'tax_ids': self._get_l10n_in_fiscal_tax_vals('fiscal_position_in_lut_sez'),
|
||||
},
|
||||
}
|
||||
|
||||
def _get_l10n_in_fiscal_tax_vals(self, fiscal_position_xml_ids):
|
||||
rates = [1, 2, 5, 12, 18, 28, 40]
|
||||
taxes_xml_ids = []
|
||||
|
||||
if fiscal_position_xml_ids == 'fiscal_position_in_intra_state':
|
||||
taxes_xml_ids = [f"sgst_{tax_type}_{rate}" for tax_type in ["sale", "purchase"] for rate in rates]
|
||||
elif fiscal_position_xml_ids == 'fiscal_position_in_inter_state':
|
||||
taxes_xml_ids = [f"igst_{tax_type}_{rate}" for tax_type in ["sale", "purchase"] for rate in rates]
|
||||
elif fiscal_position_xml_ids == 'fiscal_position_in_export_sez_in':
|
||||
taxes_xml_ids = [f"igst_sale_{rate}_sez_exp" for rate in rates] + [f"igst_purchase_{rate}" for rate in rates] + ['igst_sale_0_sez_exp']
|
||||
elif fiscal_position_xml_ids == 'fiscal_position_in_lut_sez':
|
||||
taxes_xml_ids = [f"igst_sale_{rate}_sez_exp_lut" for rate in rates] + ['igst_sale_0_sez_exp_lut']
|
||||
elif fiscal_position_xml_ids == 'fiscal_position_in_lut_sez_1':
|
||||
taxes_xml_ids = [f"igst_sale_{rate}_sez_lut" for rate in rates] + ['igst_sale_0_sez_lut']
|
||||
return [Command.set(taxes_xml_ids)]
|
||||
|
||||
def _post_load_data(self, template_code, company, template_data):
|
||||
super()._post_load_data(template_code, company, template_data)
|
||||
if template_code == 'in':
|
||||
company = company or self.env.company
|
||||
company._update_l10n_in_is_gst_registered()
|
||||
|
||||
# The COA (Chart of Accounts) data is loaded after the initial compute methods are called.
|
||||
# During initial journal setup, the payment methods and accounts may not exist yet,
|
||||
# causing the payment method lines to not be properly configured.
|
||||
# We call these helper methods again in _post_load_data to ensure all payment method lines
|
||||
# are correctly assigned once all COA data is fully available.
|
||||
bank_journals = company.bank_journal_ids
|
||||
bank_journals._update_payment_method_lines("inbound")
|
||||
bank_journals._update_payment_method_lines("outbound")
|
||||
|
|
@ -1,10 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class UoM(models.Model):
|
||||
class UomUom(models.Model):
|
||||
_inherit = "uom.uom"
|
||||
|
||||
# As per GST Rules you need to Specify UQC given by GST.
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_message_tree_audit_log" model="ir.ui.view">
|
||||
<field name="name">mail.message.tree.inherit.audit.log</field>
|
||||
<field name="model">mail.message</field>
|
||||
<field name="priority">99</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree edit="0" delete="0" create="0" action="action_open_document" type="object">
|
||||
<field name="res_id" invisible="1"/>
|
||||
<field name="date"/>
|
||||
<field name="author_id" widget="many2one_avatar"/>
|
||||
<field name="l10n_in_audit_log_account_move_id"/>
|
||||
<field name="l10n_in_audit_log_preview"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_message_tree_audit_log_search">
|
||||
<field name="name">mail.message.search</field>
|
||||
<field name="model">mail.message</field>
|
||||
<field name="priority">99</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Messages Search">
|
||||
<field name="l10n_in_audit_log_account_move_id"/>
|
||||
<field name="author_id"/>
|
||||
<field name="date" string="Date"/>
|
||||
<filter string="Update Only" name="update_only" domain="[('tracking_value_ids', '!=', False)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="date" name="group_by_date" domain="[]" context="{'group_by': 'date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_l10n_in_audit_trail_report" model="ir.actions.act_window">
|
||||
<field name="name">Audit trail</field>
|
||||
<field name="res_model">mail.message</field>
|
||||
<field name="view_id" ref="view_message_tree_audit_log"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[
|
||||
('model', '=', 'account.move'),
|
||||
('message_type', '=', 'notification'),
|
||||
('l10n_in_audit_log_account_move_id', '!=', False),
|
||||
]</field>
|
||||
<field name="search_view_id" ref="view_message_tree_audit_log_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="l10n_in_audit_trail_report_menu" name="Audit trail" action="action_l10n_in_audit_trail_report" parent="l10n_in.account_reports_in_statements_menu" sequence="2"
|
||||
groups="account.group_account_readonly"/>
|
||||
</odoo>
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_port_code_user,port.code.user,model_l10n_in_port_code,base.group_user,1,0,0,0
|
||||
access_port_code_account_manager,port.code.user,model_l10n_in_port_code,account.group_account_manager,1,1,1,1
|
||||
l10n_in.access_l10n_in_withhold_wizard,access_l10n_in_withhold_wizard,l10n_in.model_l10n_in_withhold_wizard,account.group_account_invoice,1,1,1,1
|
||||
access_l10n_in_section_alert_account_readonly,l10n_in.section.alert.account.readonly,model_l10n_in_section_alert,account.group_account_readonly,1,0,0,0
|
||||
access_l10n_in_section_alert_account_manager,l10n_in.section.alert.account.manager,model_l10n_in_section_alert,account.group_account_manager,1,1,1,1
|
||||
access_l10n_in_pan_entity,l10n_in.pan.entity,model_l10n_in_pan_entity,base.group_user,1,1,1,1
|
||||
|
|
|
|||
|
|
|
@ -2,6 +2,5 @@
|
|||
<odoo>
|
||||
<record model="res.groups" id="group_l10n_in_reseller">
|
||||
<field name="name">Manage Reseller(E-Commerce)</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 60 KiB |
|
|
@ -0,0 +1,116 @@
|
|||
import { AutoComplete } from "@web/core/autocomplete/autocomplete";
|
||||
import { useChildRef } from "@web/core/utils/hooks";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { CharField, charField } from "@web/views/fields/char/char_field";
|
||||
import { useInputField } from "@web/views/fields/input_field_hook";
|
||||
|
||||
const l10N_IN_HSN_SERVICE_URL = "https://services.gst.gov.in/commonservices/hsn/search/qsearch";
|
||||
|
||||
export class L10nInHsnAutoComplete extends CharField {
|
||||
static template = "l10n_in.hsnAutoComplete";
|
||||
static components = {
|
||||
...CharField.components,
|
||||
AutoComplete,
|
||||
};
|
||||
static props = {
|
||||
...CharField.props,
|
||||
l10nInHsnDescription: { type: String, optional: true },
|
||||
};
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.inputRef = useChildRef();
|
||||
useInputField({
|
||||
getValue: () => this.props.record.data[this.props.name] || "",
|
||||
parse: (v) => this.parse(v),
|
||||
ref: this.inputRef,
|
||||
});
|
||||
}
|
||||
|
||||
async getHsnSuggestions(value) {
|
||||
const suggestions = [];
|
||||
const onlyDigits = !isNaN(value) && value.indexOf(" ") < 0;
|
||||
const params = [
|
||||
{ type: "byCode", category: "null" }, // For code
|
||||
{ type: "byDesc", category: "P" }, // For products
|
||||
{ type: "byDesc", category: "S" }, // For services
|
||||
];
|
||||
const filteredParams = onlyDigits ? [params[0]] : params.slice(1);
|
||||
try {
|
||||
await Promise.all(
|
||||
filteredParams.map(async (param) => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
setTimeout(() => controller.abort(), 5000);
|
||||
const res = await fetch(
|
||||
`${l10N_IN_HSN_SERVICE_URL}?inputText=${value}&selectedType=${param.type}&category=${param.category}`,
|
||||
{ signal }
|
||||
);
|
||||
if (!res.ok) {
|
||||
throw new Error(res.statusText);
|
||||
}
|
||||
const resData = await res.json();
|
||||
for (const item of resData.data || []) {
|
||||
if (item.c.length > 3) {
|
||||
suggestions.push({
|
||||
data: {
|
||||
description: item.n,
|
||||
},
|
||||
label: item.c,
|
||||
onSelect: () => this.selectSuggestion(item.c, item.n),
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
suggestions.push({
|
||||
label: _t("Could not contact API"),
|
||||
});
|
||||
console.warn("HSN Autocomplete API error:", e);
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
get sources() {
|
||||
return [
|
||||
{
|
||||
options: async (request) => {
|
||||
if (request?.length > 2) {
|
||||
return await this.getHsnSuggestions(request);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
optionSlot: "option",
|
||||
placeholder: _t("Searching..."),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
selectSuggestion(label, description) {
|
||||
const data = { [this.props.name]: label };
|
||||
if (this.props.l10nInHsnDescription) {
|
||||
data[this.props.l10nInHsnDescription] = description;
|
||||
}
|
||||
setTimeout(() => this.props.record.update(data));
|
||||
}
|
||||
}
|
||||
|
||||
export const l10nInHsnAutoComplete = {
|
||||
...charField,
|
||||
component: L10nInHsnAutoComplete,
|
||||
supportedOptions: [
|
||||
{
|
||||
label: _t("hsn description field"),
|
||||
name: "hsn_description_field",
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
extractProps: ({ options }) => ({
|
||||
l10nInHsnDescription: options.hsn_description_field,
|
||||
}),
|
||||
};
|
||||
|
||||
registry.category("fields").add("l10n_in_hsn_autocomplete", l10nInHsnAutoComplete);
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates>
|
||||
<t t-name="l10n_in.hsnAutoComplete">
|
||||
<AutoComplete
|
||||
value="props.record.data[props.name] || ''"
|
||||
sources="sources"
|
||||
input="inputRef"
|
||||
placeholder="props.placeholder || ''"
|
||||
>
|
||||
<t t-set-slot="option" t-slot-scope="optionScope">
|
||||
<div class="text-wrap">
|
||||
<strong t-out="optionScope.label"/>
|
||||
<div t-out="optionScope.data.description"/>
|
||||
</div>
|
||||
</t>
|
||||
</AutoComplete>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
import { TestsSharedJsPython } from "@account/components/tests_shared_js_python/tests_shared_js_python";
|
||||
import { accountTaxHelpers } from "@account/helpers/account_tax";
|
||||
|
||||
patch(TestsSharedJsPython.prototype, {
|
||||
/** override **/
|
||||
processTest(params){
|
||||
if (params.test === "l10n_in_hsn_summary") {
|
||||
const document = this.populateDocument(params.document);
|
||||
return {'hsn': accountTaxHelpers.l10n_in_get_hsn_summary_table(document.lines, params.display_uom)};
|
||||
}
|
||||
return super.processTest(...arguments);
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
import { accountTaxHelpers } from "@account/helpers/account_tax";
|
||||
|
||||
patch(accountTaxHelpers, {
|
||||
/**
|
||||
* [!] Mirror of the same method in account_tax.py.
|
||||
* PLZ KEEP BOTH METHODS CONSISTENT WITH EACH OTHERS.
|
||||
*/
|
||||
l10n_in_get_hsn_summary_table(base_lines, display_uom) {
|
||||
const l10n_in_gst_tax_types = new Set();
|
||||
const items_map = {};
|
||||
|
||||
function get_base_line_grouping_key(base_line) {
|
||||
const unique_taxes_data = new Set(
|
||||
base_line.tax_details.taxes_data
|
||||
.filter(tax_data => ['igst', 'cgst', 'sgst'].includes(tax_data.tax.l10n_in_gst_tax_type))
|
||||
.map(tax_data => tax_data.tax)
|
||||
);
|
||||
const rate = [...unique_taxes_data].reduce((sum, tax) => sum + tax.amount, 0);
|
||||
|
||||
return {
|
||||
l10n_in_hsn_code: base_line.l10n_in_hsn_code,
|
||||
uom_name: base_line.product_uom_id.name,
|
||||
rate: rate,
|
||||
};
|
||||
}
|
||||
|
||||
// quantity / amount_untaxed.
|
||||
for (const base_line of base_lines) {
|
||||
const raw_key = get_base_line_grouping_key(base_line);
|
||||
if (!raw_key.l10n_in_hsn_code) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = JSON.stringify(raw_key);
|
||||
if (!(key in items_map)) {
|
||||
items_map[key] = {
|
||||
key: raw_key,
|
||||
quantity: 0.0,
|
||||
amount_untaxed: 0.0,
|
||||
tax_amount_igst: 0.0,
|
||||
tax_amount_cgst: 0.0,
|
||||
tax_amount_sgst: 0.0,
|
||||
tax_amount_cess: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
const item = items_map[key];
|
||||
item.quantity += base_line.quantity;
|
||||
item.amount_untaxed += (
|
||||
base_line.tax_details.total_excluded_currency +
|
||||
base_line.tax_details.delta_total_excluded_currency
|
||||
);
|
||||
}
|
||||
|
||||
// Tax amounts.
|
||||
function grouping_function(base_line, tax_data) {
|
||||
return tax_data ? {
|
||||
...get_base_line_grouping_key(base_line),
|
||||
l10n_in_gst_tax_type: tax_data.tax.l10n_in_gst_tax_type,
|
||||
} : null;
|
||||
}
|
||||
|
||||
const base_lines_aggregated_values = this.aggregate_base_lines_tax_details(base_lines, grouping_function);
|
||||
const values_per_grouping_key = this.aggregate_base_lines_aggregated_values(base_lines_aggregated_values);
|
||||
for (const values of Object.values(values_per_grouping_key)) {
|
||||
const grouping_key = values.grouping_key;
|
||||
if (!grouping_key || !grouping_key.l10n_in_hsn_code || !grouping_key.l10n_in_gst_tax_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = JSON.stringify({
|
||||
l10n_in_hsn_code: grouping_key.l10n_in_hsn_code,
|
||||
uom_name: grouping_key.uom_name,
|
||||
rate: grouping_key.rate,
|
||||
});
|
||||
const item = items_map[key];
|
||||
const l10n_in_gst_tax_type = grouping_key.l10n_in_gst_tax_type;
|
||||
item[`tax_amount_${l10n_in_gst_tax_type}`] += values.tax_amount_currency;
|
||||
l10n_in_gst_tax_types.add(l10n_in_gst_tax_type);
|
||||
}
|
||||
|
||||
const items = [];
|
||||
for (const values of Object.values(items_map)) {
|
||||
const item = {...values.key, ...values};
|
||||
delete item.key;
|
||||
items.push(item);
|
||||
}
|
||||
return {
|
||||
has_igst: l10n_in_gst_tax_types.has("igst"),
|
||||
has_gst: l10n_in_gst_tax_types.has("cgst") || l10n_in_gst_tax_types.has("sgst"),
|
||||
has_cess: l10n_in_gst_tax_types.has("cess"),
|
||||
nb_columns: 5 + l10n_in_gst_tax_types.size,
|
||||
display_uom: display_uom,
|
||||
items: items,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="800" width="1200" class="main-header__logo-image" fill="#A1A1A1" viewBox="-65.39955 -43.28375 566.7961 259.7025"><path fill="#5f6368" d="M206.197 84.585v50.75h-16.1V10.005h42.7a38.61 38.61 0 0127.65 10.85 34.88 34.88 0 0111.55 26.45 34.72 34.72 0 01-11.55 26.6q-11.2 10.68-27.65 10.67h-26.6zm0-59.15v43.75h27a21.28 21.28 0 0015.93-6.48 21.36 21.36 0 000-30.63 21 21 0 00-15.93-6.65h-27zm102.9 21.35q17.85 0 28.18 9.54 10.33 9.54 10.32 26.16v52.85h-15.4v-11.9h-.7q-10 14.7-26.6 14.7-14.17 0-23.71-8.4a26.82 26.82 0 01-9.54-21q0-13.31 10.06-21.17 10.06-7.86 26.86-7.88 14.34 0 23.62 5.25v-3.68a18.33 18.33 0 00-6.65-14.25 22.8 22.8 0 00-15.54-5.87q-13.49 0-21.35 11.38l-14.18-8.93q11.7-16.8 34.63-16.8zm-20.83 62.3a12.86 12.86 0 005.34 10.5 19.64 19.64 0 0012.51 4.2 25.67 25.67 0 0018.11-7.52q8-7.53 8-17.67-7.53-6-21-6-9.81 0-16.36 4.73c-4.41 3.2-6.6 7.09-6.6 11.76zm147.73-59.5l-53.76 123.55h-16.62l19.95-43.23-35.35-80.32h17.5l25.55 61.6h.35l24.85-61.6z"/><path fill="#4285f4" d="M141.137 73.645a85.79 85.79 0 00-1.24-14.64h-67.9v27.73h38.89a33.33 33.33 0 01-14.38 21.88v18h23.21c13.59-12.53 21.42-31.06 21.42-52.97z"/><path fill="#34a853" d="M71.997 144.005c19.43 0 35.79-6.38 47.72-17.38l-23.21-18c-6.46 4.38-14.78 6.88-24.51 6.88-18.78 0-34.72-12.66-40.42-29.72H7.667v18.55a72 72 0 0064.33 39.67z"/><path fill="#fbbc04" d="M31.577 85.785a43.14 43.14 0 010-27.56v-18.55H7.667a72 72 0 000 64.66z"/><path fill="#ea4335" d="M71.997 28.505a39.09 39.09 0 0127.62 10.8l20.55-20.55A69.18 69.18 0 0071.997.005a72 72 0 00-64.33 39.67l23.91 18.55c5.7-17.06 21.64-29.72 40.42-29.72z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="800" width="1200" viewBox="-2.5242255 -1.318595 21.876621 7.91157"><g fill="none"><path d="M16.77137 1.55572c-.15275-.43674-.56903-.75036-1.05798-.75036h-.01023c-.31785 0-.6043.13229-.80821.34466-.20426-.21237-.49072-.34466-.80822-.34466h-.01023c-.2794 0-.5348.1023-.73095.27164V.99092c-.0067-.08573-.07726-.1531-.1644-.1531h-.75c-.09172 0-.1658.07408-.1658.16615v4.07282c0 .09207.07408.16616.1658.16616h.75c.08361 0 .1524-.06244.16334-.14323l-.00035-2.92382a.26293.26293 0 01.0014-.02928c.012-.13053.1076-.23777.2586-.25118h.13827c.06315.00564.11642.02787.1584.06138.06526.05186.1016.13159.1016.21908l.00282 2.90936c0 .09207.07443.1665.1658.1665h.75c.08856 0 .16052-.07055.16476-.15839l-.00035-2.92135c-.00036-.09596.0441-.18274.12206-.23425.03845-.0247.08467-.04127.13793-.04621h.13829c.16228.01411.26035.13723.26.28046l.00282 2.90548c0 .09207.07444.16615.1658.16615h.75001c.09137 0 .1658-.07408.1658-.16615V1.95259c0-.21308-.02398-.30374-.05679-.39687M11.69398.84843h-.42897V.15134C11.265.06773 11.19727 0 11.11366 0c-.00988 0-.0194.0014-.02857.00317-.47555.13053-.3803.78917-1.24848.84526h-.08432c-.0127 0-.02469.00176-.03633.00423h-.0007l.0007.00035c-.07409.01659-.12982.0822-.12982.16123v.75c0 .09137.07443.1658.16615.1658h.45262l-.0007 3.1803c0 .09066.07337.16404.16403.16404h.74154c.09031 0 .1637-.07338.1637-.16404l.00034-3.1803h.42016c.09137 0 .1658-.07443.1658-.1658v-.75c0-.09137-.07443-.1658-.1658-.1658" fill="#00BAF2"/><path d="M8.99555.84843h-.75c-.09137 0-.16546.07444-.16546.1658v1.55082c-.00176.09595-.07937.17286-.17568.17286h-.31397c-.09737 0-.17604-.07832-.17604-.17569l-.00282-1.54798c0-.09137-.07444-.1658-.1658-.1658h-.75001c-.09172 0-.1658.07443-.1658.1658v1.69968c0 .64558.46037 1.10596 1.1063 1.10596 0 0 .48472 0 .49954.00282.08749.00988.15557.08325.15557.17356 0 .08926-.06667.16228-.1531.17322-.00423.0007-.00811.00176-.0127.00247l-1.09679.00388c-.09172 0-.1658.07443-.1658.1658v.74966c0 .09172.07408.1658.1658.1658h1.22626c.64628 0 1.1063-.46002 1.1063-1.10595v-3.1369c0-.09137-.07408-.1658-.1658-.1658M1.73602 2.22268v.46285c0 .097-.07867.17603-.17568.17603l-.4759.00035v-.92745h.4759c.09701 0 .17568.07831.17568.17568zM1.80199.84825H.16263C.07267.84825 0 .92128 0 1.01088v.73484c0 .00141.00035.00282.00035.00423 0 .00353-.00035.00706-.00035.01023v3.32599c0 .09031.06773.16405.1517.16616h.7641c.09137 0 .1658-.07408.1658-.1658l.00283-1.13983h.71755c.60042 0 1.01882-.41663 1.01882-1.01953V1.8692c0-.6029-.4184-1.02094-1.01882-1.02094zM4.8479 3.98346v.11712c0 .00953-.0014.0187-.00281.02752a.17498.17498 0 01-.00706.02434c-.02329.06562-.0889.11324-.16687.11324h-.3122c-.09737 0-.17674-.07408-.17674-.1651v-.14146c0-.00176-.00036-.00353-.00036-.00529l.00036-.37641v-.11784l.00035-.00105c.00035-.09067.07902-.16404.17639-.16404h.3122c.09772 0 .17675.07373.17675.1651zM4.72868.85256h-1.0407c-.09207 0-.1665.06985-.1665.15557v.29175c0 .00176.00035.00388.00035.00564 0 .00212-.00036.00423-.00036.00635v.3997c0 .09067.07903.16475.1764.16475h.99094c.07832.01235.14041.0695.14923.15875v.09666c-.00882.08502-.0702.1471-.145.15416h-.4907c-.65264 0-1.1176.43357-1.1176 1.04246v.87207c0 .60537.3997 1.0361 1.04774 1.0361h1.35996c.24412 0 .44203-.18485.44203-.41239V1.97827c0-.69003-.3556-1.12571-1.2058-1.12571z" fill="#1F336B"/></g></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="800" width="1200" xml:space="preserve" y="0" x="0" id="Layer_2" version="1.1" viewBox="-19.77372 -8.949675 171.37224 53.69805"><style id="style28" type="text/css">.st0{fill:#5f259f}</style><g transform="translate(.0248 -6.071)" id="g34"><circle r="17.9" id="ellipse30" cy="24" cx="17.9" class="st0" transform="matrix(.2298 -.9732 .9732 .2298 -9.5957 35.8754)"/><path id="path32" d="M90.5 34.2v-6.5c0-1.6-.6-2.4-2.1-2.4-.6 0-1.3.1-1.7.2V35c0 .3-.3.6-.6.6h-2.3c-.3 0-.6-.3-.6-.6V23.9c0-.4.3-.7.6-.8 1.5-.5 3-.8 4.6-.8 3.6 0 5.6 1.9 5.6 5.4v7.4c0 .3-.3.6-.6.6H92c-.9 0-1.5-.7-1.5-1.5zm9-3.9l-.1.9c0 1.2.8 1.9 2.1 1.9 1 0 1.9-.3 2.9-.8.1 0 .2-.1.3-.1.2 0 .3.1.4.2.1.1.3.4.3.4.2.3.4.7.4 1 0 .5-.3 1-.7 1.2-1.1.6-2.4.9-3.8.9-1.6 0-2.9-.4-3.9-1.2-1-.9-1.6-2.1-1.6-3.6v-3.9c0-3.1 2-5 5.4-5 3.3 0 5.2 1.8 5.2 5v2.4c0 .3-.3.6-.6.6h-6.3zm-.1-2.2h3.8v-1c0-1.2-.7-2-1.9-2s-1.9.7-1.9 2zm25.5 2.2l-.1.9c0 1.2.8 1.9 2.1 1.9 1 0 1.9-.3 2.9-.8.1 0 .2-.1.3-.1.2 0 .3.1.4.2.1.1.3.4.3.4.2.3.4.7.4 1 0 .5-.3 1-.7 1.2-1.1.6-2.4.9-3.8.9-1.6 0-2.9-.4-3.9-1.2-1-.9-1.6-2.1-1.6-3.6v-3.9c0-3.1 2-5 5.4-5 3.3 0 5.2 1.8 5.2 5v2.4c0 .3-.3.6-.6.6h-6.3zm-.1-2.2h3.8v-1c0-1.2-.7-2-1.9-2s-1.9.7-1.9 2zM66 35.7h1.4c.3 0 .6-.3.6-.6v-7.4c0-3.4-1.8-5.4-4.8-5.4-.9 0-1.9.2-2.5.4V19c0-.8-.7-1.5-1.5-1.5h-1.4c-.3 0-.6.3-.6.6v17c0 .3.3.6.6.6h2.3c.3 0 .6-.3.6-.6v-9.4c.5-.2 1.2-.3 1.7-.3 1.5 0 2.1.7 2.1 2.4v6.5c.1.7.7 1.4 1.5 1.4zm15.1-8.4V31c0 3.1-2.1 5-5.6 5-3.4 0-5.6-1.9-5.6-5v-3.7c0-3.1 2.1-5 5.6-5 3.5 0 5.6 1.9 5.6 5zm-3.5 0c0-1.2-.7-2-2-2s-2 .7-2 2V31c0 1.2.7 1.9 2 1.9s2-.7 2-1.9zm-22.3-1.7c0 3.2-2.4 5.4-5.6 5.4-.8 0-1.5-.1-2.2-.4v4.5c0 .3-.3.6-.6.6h-2.3c-.3 0-.6-.3-.6-.6V19.2c0-.4.3-.7.6-.8 1.5-.5 3-.8 4.6-.8 3.6 0 6.1 2.2 6.1 5.6zM51.7 23c0-1.6-1.1-2.4-2.6-2.4-.9 0-1.5.3-1.5.3v6.6c.6.3.9.4 1.6.4 1.5 0 2.6-.9 2.6-2.4V23zm68.2 2.6c0 3.2-2.4 5.4-5.6 5.4-.8 0-1.5-.1-2.2-.4v4.5c0 .3-.3.6-.6.6h-2.3c-.3 0-.6-.3-.6-.6V19.2c0-.4.3-.7.6-.8 1.5-.5 3-.8 4.6-.8 3.6 0 6.1 2.2 6.1 5.6zm-3.6-2.6c0-1.6-1.1-2.4-2.6-2.4-.9 0-1.5.3-1.5.3v6.6c.6.3.9.4 1.6.4 1.5 0 2.6-.9 2.6-2.4V23z" class="st0"/></g><path id="path36" d="M26.0248 13.229c0-.7-.6-1.3-1.3-1.3h-2.4l-5.5-6.3c-.5-.6-1.3-.8-2.1-.6l-1.9.6c-.3.1-.4.5-.2.7l6 5.7h-9.1c-.3 0-.5.2-.5.5v1c0 .7.6 1.3 1.3 1.3h1.4v4.8c0 3.6 1.9 5.7 5.1 5.7 1 0 1.8-.1 2.8-.5v3.2c0 .9.7 1.6 1.6 1.6h1.4c.3 0 .6-.3.6-.6v-14.3h2.3c.3 0 .5-.2.5-.5zm-6.4 8.6c-.6.3-1.4.4-2 .4-1.6 0-2.4-.8-2.4-2.6v-4.8h4.4z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="60" fill-rule="evenodd"><path d="M95.678 42.9L110 29.835l-6.784-13.516z" fill="#097939"/><path d="M90.854 42.9l14.322-13.065-6.784-13.516z" fill="#ed752e"/><path d="M22.41 16.47l-6.03 21.475 21.407.15 5.88-21.625h5.427l-7.05 25.14c-.27.96-1.298 1.74-2.295 1.74H12.31c-1.664 0-2.65-1.3-2.2-2.9l6.724-23.98zm66.182-.15h5.427l-7.538 27.03h-5.58zM49.698 27.582l27.136-.15 1.81-5.707H51.054l1.658-5.256 29.4-.27c1.83-.017 2.92 1.4 2.438 3.167L81.78 29.49c-.483 1.766-2.36 3.197-4.19 3.197H53.316L50.454 43.8h-5.28z" fill="#747474"/></svg>
|
||||
|
After Width: | Height: | Size: 593 B |
8
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/__init__.py
Normal file
8
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
from . import common
|
||||
from . import test_hsn_summary
|
||||
from . import test_partner_details_on_invoice
|
||||
from . import test_l10n_in_fiscal_position
|
||||
from . import test_check_status
|
||||
from . import test_tds_tcs_alert
|
||||
from . import test_gstr_section
|
||||
from . import test_invoice_label
|
||||
271
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/common.py
Normal file
271
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/common.py
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
from datetime import date
|
||||
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo import Command
|
||||
|
||||
|
||||
class L10nInTestInvoicingCommon(AccountTestInvoicingCommon):
|
||||
@classmethod
|
||||
@AccountTestInvoicingCommon.setup_country('in')
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.maxDiff = None
|
||||
cls.test_date = date(2023, 5, 20)
|
||||
|
||||
# === Countries === #
|
||||
cls.country_in = cls.env.ref('base.in')
|
||||
cls.country_us = cls.env.ref('base.us')
|
||||
|
||||
# === States === #
|
||||
cls.state_in_gj = cls.env.ref('base.state_in_gj')
|
||||
cls.state_in_mh = cls.env.ref('base.state_in_mh')
|
||||
cls.state_in_hp = cls.env.ref('base.state_in_hp')
|
||||
|
||||
# === Companies === #
|
||||
cls.default_company = cls.company_data['company']
|
||||
cls.default_company.write({
|
||||
'name': "Default Company",
|
||||
'state_id': cls.state_in_gj.id,
|
||||
'vat': "24AAGCC7144L6ZE",
|
||||
'street': "Khodiyar Chowk",
|
||||
'street2': "Sala Number 3",
|
||||
'city': "Amreli",
|
||||
'zip': "365220",
|
||||
'l10n_in_is_gst_registered': True,
|
||||
'l10n_in_tds_feature': True,
|
||||
'l10n_in_tcs_feature': True,
|
||||
})
|
||||
|
||||
cls.outside_in_company = cls.env['res.company'].create({
|
||||
'name': 'Outside India Company',
|
||||
'country_id': cls.country_us.id,
|
||||
})
|
||||
|
||||
cls.user.write({
|
||||
'company_ids': [cls.default_company.id, cls.outside_in_company.id],
|
||||
'company_id': cls.default_company.id,
|
||||
})
|
||||
|
||||
# === Partners === #
|
||||
cls.partner_a.write({
|
||||
'name': "Partner Intra State",
|
||||
'vat': '24ABCPM8965E1ZE',
|
||||
'state_id': cls.default_company.state_id.id,
|
||||
'country_id': cls.country_in.id,
|
||||
'street': "Karansinhji Rd",
|
||||
'street2': "Karanpara",
|
||||
'city': "Rajkot",
|
||||
'zip': "360001",
|
||||
})
|
||||
|
||||
cls.partner_b.write({
|
||||
'vat': '27DJMPM8965E1ZE',
|
||||
'state_id': cls.state_in_mh.id,
|
||||
'country_id': cls.country_in.id,
|
||||
'street': "Sangeet Samrat Naushad Ali Rd",
|
||||
'city': "Mumbai",
|
||||
'zip': "400052",
|
||||
})
|
||||
|
||||
cls.partner_foreign = cls.env['res.partner'].create({
|
||||
'name': "Foreign Partner",
|
||||
'country_id': cls.country_us.id,
|
||||
'state_id': cls.env.ref("base.state_us_1").id,
|
||||
'street': "351 Horner Chapel Rd",
|
||||
'city': "Peebles",
|
||||
'zip': "45660",
|
||||
})
|
||||
|
||||
cls.partner_foreign_no_state = cls.env['res.partner'].create({
|
||||
'name': "Foreign Partner Without State",
|
||||
'country_id': cls.country_us.id,
|
||||
# No state_id defined
|
||||
})
|
||||
cls.sez_partner = cls.env['res.partner'].create({
|
||||
'name': 'SEZ Partner',
|
||||
'vat': '36AAAAA1234AAZA',
|
||||
'l10n_in_gst_treatment': 'special_economic_zone',
|
||||
'street': 'Block no. 402',
|
||||
'city': 'Some city',
|
||||
'zip': '500002',
|
||||
'state_id': cls.env.ref('base.state_in_gj').id,
|
||||
'country_id': cls.env.ref('base.in').id,
|
||||
})
|
||||
|
||||
# === Taxes === #
|
||||
AccountChartTemplate = cls.env['account.chart.template']
|
||||
cls.sgst_sale_5 = AccountChartTemplate.ref('sgst_sale_5')
|
||||
cls.sgst_purchase_5 = AccountChartTemplate.ref('sgst_purchase_5')
|
||||
cls.igst_sale_5 = AccountChartTemplate.ref('igst_sale_5')
|
||||
cls.igst_sale_18 = AccountChartTemplate.ref('igst_sale_18')
|
||||
cls.sgst_sale_18 = AccountChartTemplate.ref('sgst_sale_18')
|
||||
cls.igst_sale_18_rcm = AccountChartTemplate.ref('igst_sale_18_rc')
|
||||
cls.igst_sale_18_sez_lut = AccountChartTemplate.ref('igst_sale_18_sez_lut')
|
||||
cls.igst_sale_18_sez_exp_lut = AccountChartTemplate.ref('igst_sale_18_sez_exp_lut')
|
||||
cls.igst_sale_18_sez_exp = AccountChartTemplate.ref('igst_sale_18_sez_exp')
|
||||
cls.igst_sale_18_sez_exp_inc = cls.igst_sale_18_sez_exp.copy({'price_include_override': 'tax_included'})
|
||||
cls.gst_with_cess = (
|
||||
AccountChartTemplate.ref("sgst_sale_12")
|
||||
+ AccountChartTemplate.ref("cess_5_plus_1591_sale")
|
||||
)
|
||||
cls.exempt = AccountChartTemplate.ref('exempt_sale')
|
||||
|
||||
# === Products === #
|
||||
cls.product_a.write({
|
||||
"l10n_in_hsn_code": "111111",
|
||||
'taxes_id': cls.sgst_sale_5,
|
||||
'supplier_taxes_id': cls.sgst_purchase_5,
|
||||
})
|
||||
|
||||
cls.product_b.write({
|
||||
"l10n_in_hsn_code": "111111",
|
||||
'uom_id': cls.env.ref('uom.product_uom_unit').id,
|
||||
'lst_price': 1000.0,
|
||||
'standard_price': 1000.0,
|
||||
'taxes_id': cls.sgst_sale_5.ids,
|
||||
'supplier_taxes_id': cls.sgst_purchase_5.ids,
|
||||
})
|
||||
cls.product_with_cess = cls.env["product.product"].create({
|
||||
"name": "product_with_cess",
|
||||
"uom_id": cls.env.ref("uom.product_uom_unit").id,
|
||||
"lst_price": 1000.0,
|
||||
"standard_price": 800.0,
|
||||
"property_account_income_id": cls.company_data["default_account_revenue"].id,
|
||||
"property_account_expense_id": cls.company_data["default_account_expense"].id,
|
||||
"taxes_id": [Command.set(cls.gst_with_cess.ids)],
|
||||
"supplier_taxes_id": [Command.set(cls.sgst_purchase_5.ids)],
|
||||
"l10n_in_hsn_code": "333333",
|
||||
})
|
||||
|
||||
# === Fiscal Positions === #
|
||||
cls.fp_in_intra_state = cls.env["account.chart.template"].ref('fiscal_position_in_intra_state')
|
||||
cls.fp_in_inter_state = cls.env["account.chart.template"].ref('fiscal_position_in_inter_state')
|
||||
cls.fp_in_export = cls.env["account.chart.template"].ref('fiscal_position_in_export_sez_in')
|
||||
|
||||
# === Invoices === #
|
||||
cls.invoice_a = cls.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=cls.partner_a,
|
||||
amounts=[110, 500],
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_b = cls.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=cls.partner_b,
|
||||
amounts=[250, 600],
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_c = cls.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=cls.partner_foreign,
|
||||
amounts=[300, 740],
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_d = cls.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=cls.partner_foreign_no_state,
|
||||
amounts=[100, 200],
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_with_rcm = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.partner_b,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_rcm,
|
||||
)
|
||||
|
||||
cls.invoice_with_sez_lut = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.sez_partner,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_sez_lut,
|
||||
)
|
||||
|
||||
cls.invoice_with_sez_without_lut = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.sez_partner,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_with_export_lut = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.partner_foreign,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_sez_exp_lut,
|
||||
)
|
||||
|
||||
cls.invoice_with_export_without_lut = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.partner_foreign,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_sez_exp,
|
||||
)
|
||||
cls.invoice_with_export_without_lut_inc = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.partner_foreign,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_sez_exp_inc,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _set_vals_and_post(cls, move, ref=None, line_vals=None, post=True, irn=None):
|
||||
if ref:
|
||||
move.ref = ref
|
||||
if irn:
|
||||
move.l10n_in_irn_number = irn
|
||||
|
||||
if line_vals:
|
||||
move.write({'invoice_line_ids': [Command.update(line.id, line_vals) for line in move.line_ids]})
|
||||
|
||||
if post:
|
||||
move.action_post()
|
||||
return move
|
||||
|
||||
@classmethod
|
||||
def _init_inv(cls, move_type='out_invoice', company=None, ref=None, partner=None, taxes=None, invoice_date=None, products=None, line_vals=None, post=True, irn=None):
|
||||
return cls._set_vals_and_post(
|
||||
move=cls.init_invoice(
|
||||
move_type,
|
||||
products=products or cls.product_a,
|
||||
invoice_date=invoice_date or cls.test_date,
|
||||
taxes=taxes,
|
||||
company=company or cls.default_company,
|
||||
partner=partner,
|
||||
),
|
||||
ref=ref,
|
||||
irn=irn,
|
||||
line_vals=line_vals,
|
||||
post=post
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _create_credit_note(cls, inv, ref=None, credit_note_date=None, line_vals=None, post=True):
|
||||
move = inv._reverse_moves()
|
||||
move.invoice_date = credit_note_date or cls.test_date
|
||||
|
||||
return cls._set_vals_and_post(
|
||||
move=move,
|
||||
ref=ref,
|
||||
line_vals=line_vals,
|
||||
post=post
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _create_debit_note(cls, inv, ref=None, debit_note_date=None, line_vals=None):
|
||||
move_debit_note_wiz = cls.env['account.debit.note'].with_context(
|
||||
active_model="account.move",
|
||||
active_ids=inv.ids
|
||||
).create({
|
||||
'date': debit_note_date or cls.test_date,
|
||||
'reason': 'no reason',
|
||||
'copy_lines': True,
|
||||
})
|
||||
move_debit_note_wiz.create_debit()
|
||||
|
||||
return cls._set_vals_and_post(move=inv.debit_note_ids[0], ref=ref, line_vals=line_vals)
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
from unittest.mock import patch
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.addons.l10n_in.models.iap_account import IapAccount
|
||||
from odoo.tests.common import TransactionCase, tagged
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import mute_logger
|
||||
from datetime import date
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestGSTStatusFeature(TransactionCase):
|
||||
def setUp(self):
|
||||
self.partner1 = self.env["res.partner"].create(
|
||||
{"name": "Active GSTIN", "vat": "36AAACM4154G1ZO"}
|
||||
)
|
||||
self.partner2 = self.env["res.partner"].create(
|
||||
{"name": "Cancelled GSTIN", "vat": "19AABCT1332L2ZD"}
|
||||
)
|
||||
self.partner3 = self.env["res.partner"].create(
|
||||
{"name": "Invalid GSTIN", "vat": "19AACCT6304M1ZB"}
|
||||
)
|
||||
self.partner4 = self.env["res.partner"].create(
|
||||
{"name": "No Records GSTIN", "vat": "19AACCT6304M1DB"}
|
||||
)
|
||||
self.partner5 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Partner Vat Reset",
|
||||
"vat": "36AAACM4154G1ZO",
|
||||
"country_id": self.env.ref('base.in').id,
|
||||
"l10n_in_gstin_verified_status": "active",
|
||||
"l10n_in_gstin_verified_date": "2024-06-01",
|
||||
}
|
||||
)
|
||||
self.mock_responses = {
|
||||
"active": {
|
||||
"data": {"sts": "Active"}
|
||||
},
|
||||
"cancelled": {
|
||||
"data": {"sts": "Cancelled"}
|
||||
},
|
||||
"invalid": {
|
||||
"error": [{"code": "SWEB_9035", "message": "Invalid GSTIN / UID"}],
|
||||
},
|
||||
"no_records": {
|
||||
"error": [{"code": "FO8000", "message": "No records found for the provided GSTIN."}],
|
||||
},
|
||||
}
|
||||
self.env.company.l10n_in_gstin_status_feature = True
|
||||
self.env.company.account_fiscal_country_id = self.env.ref("base.in")
|
||||
|
||||
@freeze_time('2024-05-20')
|
||||
@mute_logger('odoo.addons.l10n_in.models.res_partner')
|
||||
def check_gstin_status(self, partner, expected_status, mock_response, raises_exception=False):
|
||||
with patch.object(IapAccount, "_l10n_in_connect_to_server", return_value=mock_response):
|
||||
if raises_exception:
|
||||
with self.assertRaises(UserError):
|
||||
partner.action_l10n_in_verify_gstin_status()
|
||||
else:
|
||||
partner.action_l10n_in_verify_gstin_status()
|
||||
self.assertEqual(partner.l10n_in_gstin_verified_status, expected_status)
|
||||
self.assertEqual(partner.l10n_in_gstin_verified_date, date(2024, 5, 20))
|
||||
|
||||
def test_gstin_status(self):
|
||||
"""Test GSTIN status for various cases"""
|
||||
self.check_gstin_status(
|
||||
self.partner1,
|
||||
expected_status=True,
|
||||
mock_response=self.mock_responses["active"]
|
||||
)
|
||||
self.check_gstin_status(
|
||||
self.partner2,
|
||||
expected_status=False,
|
||||
mock_response=self.mock_responses["cancelled"]
|
||||
)
|
||||
self.check_gstin_status(
|
||||
self.partner3,
|
||||
expected_status=False,
|
||||
raises_exception=True,
|
||||
mock_response=self.mock_responses["invalid"],
|
||||
)
|
||||
self.check_gstin_status(
|
||||
self.partner4,
|
||||
expected_status=False,
|
||||
raises_exception=True,
|
||||
mock_response=self.mock_responses["no_records"],
|
||||
)
|
||||
124
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/test_gstr_section.py
Normal file
124
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/test_gstr_section.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
from datetime import date
|
||||
|
||||
from odoo import Command
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
TEST_DATE = date(2025, 6, 8)
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestGstrSection(L10nInTestInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.partner_b.l10n_in_gst_treatment = "regular"
|
||||
cls.partner_foreign.l10n_in_gst_treatment = 'overseas'
|
||||
cls.sez_partner = cls.partner_a.copy({"l10n_in_gst_treatment": "special_economic_zone"})
|
||||
cls.large_unregistered_partner = cls.partner_a.copy({"state_id": cls.state_in_mh.id, "vat": None, "l10n_in_gst_treatment": "unregistered"})
|
||||
|
||||
ChartTemplate = cls.env['account.chart.template']
|
||||
cls.nil_rated_tax = ChartTemplate.ref('nil_rated_sale')
|
||||
cls.igst_lut_sale_28_sez = ChartTemplate.ref('igst_sale_28_sez_lut')
|
||||
cls.igst_rc_sale_18 = ChartTemplate.ref('igst_sale_18_rc')
|
||||
cls.igst_exp_sale_18 = ChartTemplate.ref('igst_sale_18_sez_exp')
|
||||
|
||||
def test_gstr_sections(self):
|
||||
def assert_line_sections(lines, expected_sections):
|
||||
for line in lines.filtered(lambda l: l.display_type in ('product', 'tax')):
|
||||
if line.display_type == 'product':
|
||||
tax_types = {tax.l10n_in_tax_type for tax in line.tax_ids}
|
||||
matched = tax_types & expected_sections.keys()
|
||||
if matched:
|
||||
expected = expected_sections[matched.pop()]
|
||||
else:
|
||||
expected = expected_sections.get('with_gst_tag')
|
||||
else: # tax line
|
||||
expected = expected_sections['with_gst_tag'] if line.tax_tag_ids else expected_sections['no_tag']
|
||||
self.assertEqual(line.l10n_in_gstr_section, expected)
|
||||
|
||||
# SEZ without payment with Nil Rated
|
||||
sez_invoice = self._init_inv(
|
||||
partner=self.sez_partner,
|
||||
taxes=self.igst_lut_sale_28_sez,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
post=False,
|
||||
invoice_date=TEST_DATE,
|
||||
)
|
||||
sez_invoice.write({
|
||||
'invoice_line_ids': [Command.create({
|
||||
'product_id': self.product_b.id,
|
||||
'account_id': sez_invoice.invoice_line_ids[0].account_id.id,
|
||||
'price_unit': 500,
|
||||
'quantity': 1,
|
||||
'tax_ids': [(6, 0, [self.nil_rated_tax.id])],
|
||||
})],
|
||||
})
|
||||
sez_invoice.action_post()
|
||||
assert_line_sections(sez_invoice.line_ids, {
|
||||
'with_gst_tag': 'sale_sez_wop',
|
||||
'nil_rated': 'sale_nil_rated',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
sez_credit_note = self._create_credit_note(inv=sez_invoice)
|
||||
assert_line_sections(sez_credit_note.line_ids, {
|
||||
'with_gst_tag': 'sale_cdnr_sez_wop',
|
||||
'nil_rated': 'sale_nil_rated',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
# Export with payment
|
||||
exp_invoice = self._init_inv(
|
||||
partner=self.partner_foreign,
|
||||
taxes=self.igst_exp_sale_18,
|
||||
line_vals={'price_unit': 3000, 'quantity': 1},
|
||||
invoice_date=TEST_DATE,
|
||||
)
|
||||
assert_line_sections(exp_invoice.line_ids, {
|
||||
'with_gst_tag': 'sale_exp_wp',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
exp_credit_note = self._create_credit_note(inv=exp_invoice)
|
||||
assert_line_sections(exp_credit_note.line_ids, {
|
||||
'with_gst_tag': 'sale_cdnur_exp_wp',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
# B2B RCM
|
||||
b2b_rcm_invoice = self._init_inv(
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_rc_sale_18,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
invoice_date=TEST_DATE,
|
||||
)
|
||||
assert_line_sections(b2b_rcm_invoice.line_ids, {
|
||||
'with_gst_tag': 'sale_b2b_rcm',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
b2b_rcm_credit_note = self._create_credit_note(inv=b2b_rcm_invoice)
|
||||
assert_line_sections(b2b_rcm_credit_note.line_ids, {
|
||||
'with_gst_tag': 'sale_cdnr_rcm',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
# B2CL
|
||||
b2cl_invoice = self._init_inv(
|
||||
partner=self.large_unregistered_partner,
|
||||
taxes=self.igst_sale_18,
|
||||
line_vals={'price_unit': 220000, 'quantity': 1},
|
||||
invoice_date=TEST_DATE,
|
||||
)
|
||||
assert_line_sections(b2cl_invoice.line_ids, {
|
||||
'with_gst_tag': 'sale_b2cl',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
b2cl_credit_note = self._create_credit_note(inv=b2cl_invoice, line_vals={'quantity': 0.5})
|
||||
assert_line_sections(b2cl_credit_note.line_ids, {
|
||||
'with_gst_tag': 'sale_cdnur_b2cl',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
770
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/test_hsn_summary.py
Normal file
770
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/test_hsn_summary.py
Normal file
|
|
@ -0,0 +1,770 @@
|
|||
from odoo import Command
|
||||
from odoo.addons.account.tests.common import TestTaxCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install', 'post_install_l10n')
|
||||
class TestL10nInHSNSummary(TestTaxCommon):
|
||||
|
||||
@classmethod
|
||||
@TestTaxCommon.setup_country('in')
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.company_data['company'].write({
|
||||
'l10n_in_is_gst_registered': True,
|
||||
})
|
||||
|
||||
cls.test_hsn_code_1 = '1234'
|
||||
cls.test_hsn_code_2 = '4321'
|
||||
|
||||
cls.uom_unit = cls.env.ref('uom.product_uom_unit')
|
||||
cls.uom_dozen = cls.env.ref('uom.product_uom_dozen')
|
||||
|
||||
cls.product_a.l10n_in_hsn_code = cls.test_hsn_code_1
|
||||
cls.product_b.l10n_in_hsn_code = cls.test_hsn_code_2
|
||||
cls.product_c = cls.env['product.product'].create({
|
||||
'name': 'product_c',
|
||||
'l10n_in_hsn_code': cls.test_hsn_code_1,
|
||||
'uom_id': cls.env.ref('uom.product_uom_unit').id,
|
||||
'lst_price': 1000.0,
|
||||
'property_account_income_id': cls.company_data['default_account_revenue'].id,
|
||||
})
|
||||
|
||||
ChartTemplate = cls.env['account.chart.template']
|
||||
cls.gst_5 = ChartTemplate.ref('sgst_sale_5')
|
||||
cls.gst_18 = ChartTemplate.ref('sgst_sale_18')
|
||||
cls.igst_5 = ChartTemplate.ref('igst_sale_5')
|
||||
cls.igst_18 = ChartTemplate.ref('igst_sale_18')
|
||||
cls.cess_5_plus_1591 = ChartTemplate.ref('cess_5_plus_1591_sale')
|
||||
cls.nil_rated = ChartTemplate.ref('nil_rated_sale')
|
||||
cls.exempt_0 = ChartTemplate.ref('exempt_sale')
|
||||
cls.igst_18_rc = ChartTemplate.ref('igst_sale_18_rc')
|
||||
|
||||
def _jsonify_tax(self, tax):
|
||||
# EXTENDS 'account.
|
||||
values = super()._jsonify_tax(tax)
|
||||
values['l10n_in_gst_tax_type'] = tax.l10n_in_gst_tax_type
|
||||
return values
|
||||
|
||||
def _jsonify_document_line(self, document, index, line):
|
||||
# EXTENDS 'account.
|
||||
values = super()._jsonify_document_line(document, index, line)
|
||||
values['l10n_in_hsn_code'] = line['l10n_in_hsn_code']
|
||||
return values
|
||||
|
||||
def convert_base_line_to_invoice_line(self, document, base_line):
|
||||
# EXTENDS 'account.
|
||||
values = super().convert_base_line_to_invoice_line(document, base_line)
|
||||
values['l10n_in_hsn_code'] = base_line['l10n_in_hsn_code']
|
||||
return values
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# l10n_in_hsn_summary
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def _assert_sub_test_l10n_in_hsn_summary(self, results, expected_values):
|
||||
self.assertEqual(
|
||||
{k: len(v) if k == 'items' else v for k, v in results['hsn'].items()},
|
||||
{k: len(v) if k == 'items' else v for k, v in expected_values.items()},
|
||||
)
|
||||
self.assertEqual(len(results['hsn']['items']), len(expected_values['items']))
|
||||
for item, expected_item in zip(results['hsn']['items'], expected_values['items']):
|
||||
self.assertDictEqual(item, expected_item)
|
||||
|
||||
def _create_py_sub_test_l10n_in_hsn_summary(self, document, display_uom):
|
||||
return {
|
||||
'hsn': self.env['account.tax']._l10n_in_get_hsn_summary_table(document['lines'], display_uom),
|
||||
}
|
||||
|
||||
def _create_js_sub_test_l10n_in_hsn_summary(self, document, display_uom):
|
||||
return {
|
||||
'test': 'l10n_in_hsn_summary',
|
||||
'document': self._jsonify_document(document),
|
||||
'display_uom': display_uom,
|
||||
}
|
||||
|
||||
def assert_l10n_in_hsn_summary(
|
||||
self,
|
||||
document,
|
||||
expected_values,
|
||||
display_uom=False,
|
||||
):
|
||||
self._create_assert_test(
|
||||
expected_values,
|
||||
self._create_py_sub_test_l10n_in_hsn_summary,
|
||||
self._create_js_sub_test_l10n_in_hsn_summary,
|
||||
self._assert_sub_test_l10n_in_hsn_summary,
|
||||
document,
|
||||
display_uom,
|
||||
)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# invoice l10n_in_hsn_summary
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def assert_invoice_l10n_in_hsn_summary(self, invoice, expected_values):
|
||||
results = {'hsn': {
|
||||
**invoice._l10n_in_get_hsn_summary_table(),
|
||||
# 'display_uom' is just checking if the user has the uom group. It's irrelevant to test it.
|
||||
'display_uom': expected_values['display_uom'],
|
||||
}}
|
||||
self._assert_sub_test_l10n_in_hsn_summary(results, expected_values)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Tests
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def _test_l10n_in_hsn_summary_1(self):
|
||||
""" Test GST/IGST taxes. """
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 57.5,
|
||||
'tax_amount_sgst': 57.5,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 207.0,
|
||||
'tax_amount_sgst': 207.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
# Another UOM on the second line.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 12000.0, 'product_uom_id': self.uom_dozen, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 7.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 1700.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 42.5,
|
||||
'tax_amount_sgst': 42.5,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_dozen.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 12000.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 300.0,
|
||||
'tax_amount_sgst': 300.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 207.0,
|
||||
'tax_amount_sgst': 207.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
}
|
||||
]
|
||||
}
|
||||
yield 2, document, expected_values
|
||||
|
||||
# Change GST 5% taxes to IGST.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 12000.0, 'product_uom_id': self.uom_dozen, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 8,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 7.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 1700.0,
|
||||
'tax_amount_igst': 85.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_dozen.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 12000.0,
|
||||
'tax_amount_igst': 600.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 207.0,
|
||||
'tax_amount_sgst': 207.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 3, document, expected_values
|
||||
|
||||
# Put back the UOM of the second line to unit.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 8,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 115.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 207.0,
|
||||
'tax_amount_sgst': 207.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 4, document, expected_values
|
||||
|
||||
# Change GST 18% taxes to IGST.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 6,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 115.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 414.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 5, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_1_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_1():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_1_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_1():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_2(self):
|
||||
""" Test CESS taxes in combination with GST/IGST. """
|
||||
# Need the tax to be evaluated at the end.
|
||||
self.cess_5_plus_1591.sequence = 100
|
||||
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 15.80, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18 + self.cess_5_plus_1591},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': True,
|
||||
'nb_columns': 8,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 15.8,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 1.42,
|
||||
'tax_amount_sgst': 1.42,
|
||||
'tax_amount_cess': 2.38,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
# Change GST 18% taxes to IGST.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 15.80, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18 + self.cess_5_plus_1591},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': True,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 15.8,
|
||||
'tax_amount_igst': 2.84,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 2.38,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 2, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_2_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_2():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_2_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_2():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_3(self):
|
||||
""" Test with mixed HSN codes. """
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 50.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_2, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_2, 'quantity': 2.0, 'price_unit': 50.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 3.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 200.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 18.0,
|
||||
'tax_amount_sgst': 18.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_2,
|
||||
'quantity': 3.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 200.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 18.0,
|
||||
'tax_amount_sgst': 18.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
# Change GST 18% taxes to IGST.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 50.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_2, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_2, 'quantity': 2.0, 'price_unit': 50.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 6,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 3.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 200.0,
|
||||
'tax_amount_igst': 36.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_2,
|
||||
'quantity': 3.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 200.0,
|
||||
'tax_amount_igst': 36.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 2, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_3_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_3():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_3_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_3():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_4(self):
|
||||
""" Zero rated GST or no taxes at all."""
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 5,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 2.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 0.0,
|
||||
'amount_untaxed': 700.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
# No tax to Nil Rated/exempt.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.nil_rated},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.exempt_0},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 5,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 2.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 0.0,
|
||||
'amount_untaxed': 700.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 2, document, expected_values
|
||||
|
||||
# Put one IGST 18% to get a value on the IGST column.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.exempt_0},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 6,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 350.0,
|
||||
'tax_amount_igst': 63.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 0.0,
|
||||
'amount_untaxed': 350.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 3, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_4_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_4():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_4_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_4():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_5(self):
|
||||
""" Test with discount. """
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 100.0, 'discount': 10.0, 'product_uom_id': self.uom_unit},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 5,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 0.0,
|
||||
'amount_untaxed': 90.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_5_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_5():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_5_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_5():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_6(self):
|
||||
""" Test with Sale RC tax. """
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18_rc},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 6,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 100.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_6_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_6():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_6_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_6():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def test_l10n_in_hsn_summary_manual_edit_invoice_taxes(self):
|
||||
invoice = self.env['account.move'].create({
|
||||
'move_type': 'out_invoice',
|
||||
'invoice_date': '2017-01-01',
|
||||
'partner_id': self.partner_a.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': self.product_a.id,
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set(self.gst_5.ids)],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': self.product_a.id,
|
||||
'l10n_in_hsn_code': self.test_hsn_code_2,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set(self.gst_5.ids)],
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
# Manual edition of the tax.
|
||||
sgst_tax = self.gst_5.children_tax_ids.filtered(lambda tax: tax.l10n_in_gst_tax_type == 'sgst')
|
||||
cgst_tax = self.gst_5.children_tax_ids.filtered(lambda tax: tax.l10n_in_gst_tax_type == 'cgst')
|
||||
tax_line_sgst = invoice.line_ids.filtered(lambda aml: aml.tax_line_id == sgst_tax)
|
||||
tax_line_cgst = invoice.line_ids.filtered(lambda aml: aml.tax_line_id == cgst_tax)
|
||||
payment_term = invoice.line_ids.filtered(lambda aml: aml.display_type == 'payment_term')
|
||||
invoice.line_ids = [
|
||||
Command.update(tax_line_sgst.id, {'amount_currency': tax_line_sgst.amount_currency + 1.0}),
|
||||
Command.update(tax_line_cgst.id, {'amount_currency': tax_line_cgst.amount_currency + 1.0}),
|
||||
Command.update(payment_term.id, {'amount_currency': payment_term.amount_currency - 2.0}),
|
||||
]
|
||||
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 1000.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 24.5,
|
||||
'tax_amount_sgst': 24.5,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_2,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 1000.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 24.5,
|
||||
'tax_amount_sgst': 24.5,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
from odoo import Command
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestInvoiceLabel(L10nInTestInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.partner_b.l10n_in_gst_treatment = "regular"
|
||||
cls.unregistered_partner = cls.partner_a.copy({"state_id": cls.state_in_mh.id, "vat": None, "l10n_in_gst_treatment": "unregistered"})
|
||||
|
||||
def test_invoice_label(self):
|
||||
# Regular with taxable items
|
||||
regular_taxable_invoice = self._init_inv(
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_sale_18,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
)
|
||||
invoice_label = regular_taxable_invoice._get_l10n_in_invoice_label()
|
||||
self.assertEqual(invoice_label, 'Tax Invoice')
|
||||
|
||||
# Regular with exempt items
|
||||
regular_exempt_invoice = self._init_inv(
|
||||
partner=self.partner_b,
|
||||
taxes=self.exempt,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
)
|
||||
invoice_label = regular_exempt_invoice._get_l10n_in_invoice_label()
|
||||
self.assertEqual(invoice_label, 'Bill of Supply')
|
||||
|
||||
# Regular with taxable and exempt items
|
||||
regular_mix_invoice = self._init_inv(
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_sale_18,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
post=False,
|
||||
)
|
||||
regular_mix_invoice.write({
|
||||
'invoice_line_ids': [Command.create({
|
||||
'product_id': self.product_b.id,
|
||||
'account_id': regular_mix_invoice.invoice_line_ids[0].account_id.id,
|
||||
'price_unit': 500,
|
||||
'quantity': 1,
|
||||
'tax_ids': [(6, 0, [self.exempt.id])],
|
||||
})],
|
||||
})
|
||||
regular_mix_invoice.action_post()
|
||||
invoice_label = regular_mix_invoice._get_l10n_in_invoice_label()
|
||||
self.assertEqual(invoice_label, 'Invoice')
|
||||
|
||||
# unregistered with taxable and exempt items
|
||||
unregistered_invoice = self._init_inv(
|
||||
partner=self.unregistered_partner,
|
||||
taxes=self.igst_sale_18,
|
||||
line_vals={'price_unit': 220000, 'quantity': 1},
|
||||
post=False,
|
||||
)
|
||||
unregistered_invoice.write({
|
||||
'invoice_line_ids': [Command.create({
|
||||
'product_id': self.product_b.id,
|
||||
'account_id': unregistered_invoice.invoice_line_ids[0].account_id.id,
|
||||
'price_unit': 500,
|
||||
'quantity': 1,
|
||||
'tax_ids': [(6, 0, [self.exempt.id])],
|
||||
})],
|
||||
})
|
||||
unregistered_invoice.action_post()
|
||||
invoice_label = unregistered_invoice._get_l10n_in_invoice_label()
|
||||
self.assertEqual(invoice_label, 'Invoice-cum-Bill of Supply')
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
from contextlib import contextmanager
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.tests import tagged
|
||||
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install', 'post_install_l10n')
|
||||
class TestFiscal(L10nInTestInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
# Remove fiscal position 'fiscal_pos_a' so it does not show up on invoice
|
||||
cls.partner_b.property_account_position_id = None
|
||||
|
||||
@contextmanager
|
||||
def _set_env_company(self, companies):
|
||||
cls = self.__class__
|
||||
env = cls.env(context=dict(cls.env.context, allowed_company_ids=companies.ids))
|
||||
with patch.object(cls, "env", env):
|
||||
yield
|
||||
|
||||
def _assert_in_intra_state_fiscal_with_company(self, companies):
|
||||
for company in companies:
|
||||
state = company.state_id
|
||||
name = state and 'Within %s' % state.name or 'Intra State'
|
||||
self.assertRecordValues(
|
||||
self.env['account.chart.template'].with_company(company).ref('fiscal_position_in_intra_state'),
|
||||
[{
|
||||
'name': name,
|
||||
'state_ids': state.ids,
|
||||
'company_id': company.id, # To make sure for branches don't get parent fiscal position
|
||||
'auto_apply': True
|
||||
}]
|
||||
)
|
||||
|
||||
def _assert_invoice_fiscal_position(self, fiscal_position_ref, partner, taxes=None, move_type='out_invoice', post=True):
|
||||
test_invoice = self.init_invoice(
|
||||
move_type=move_type,
|
||||
partner=partner,
|
||||
post=post,
|
||||
amounts=[110, 500],
|
||||
taxes=taxes
|
||||
)
|
||||
self.assertEqual(test_invoice.fiscal_position_id, self.env['account.chart.template'].ref(fiscal_position_ref))
|
||||
return test_invoice
|
||||
|
||||
def test_l10n_in_setting_up_company(self):
|
||||
company = self._create_company(name='Fiscal Setup Test Company')
|
||||
# Test with no state but country india
|
||||
self._assert_in_intra_state_fiscal_with_company(company)
|
||||
# Change state
|
||||
company.write({'state_id': self.default_company.state_id.id})
|
||||
self._assert_in_intra_state_fiscal_with_company(company)
|
||||
# Change State Again
|
||||
company.write({'state_id': self.env.ref('base.state_in_ap')})
|
||||
self._assert_in_intra_state_fiscal_with_company(company)
|
||||
|
||||
def test_l10n_in_auto_apply_fiscal_invoices(self):
|
||||
self._assert_in_intra_state_fiscal_with_company(self.default_company)
|
||||
|
||||
# Intra State
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_intra_state',
|
||||
partner=self.partner_a,
|
||||
taxes=self.sgst_sale_18,
|
||||
)
|
||||
# Inter State
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_inter_state',
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_sale_18,
|
||||
)
|
||||
# Outside India
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_export_sez_in',
|
||||
partner=self.partner_foreign,
|
||||
taxes=self.igst_sale_18,
|
||||
)
|
||||
|
||||
def test_l10n_in_fiscal_for_branch(self):
|
||||
branch_1 = self._create_company(
|
||||
name='Branch 1',
|
||||
parent_id=self.default_company.id,
|
||||
state_id=self.partner_b.state_id.id, # Setting Partner B state will be now Intra State for branch 1
|
||||
account_fiscal_country_id=self.country_in.id,
|
||||
)
|
||||
with self._set_env_company(self.outside_in_company):
|
||||
branch_2 = self.env['res.company'].create({
|
||||
'name': 'Branch 2',
|
||||
'parent_id': self.default_company.id,
|
||||
'account_fiscal_country_id': self.country_in.id,
|
||||
'country_id': self.country_in.id,
|
||||
})
|
||||
# Check Branch with country india and no state
|
||||
self._assert_in_intra_state_fiscal_with_company(branch_2)
|
||||
# Set state after creating branch
|
||||
branch_2.write({'state_id': self.env.ref('base.state_in_mp').id})
|
||||
self._assert_in_intra_state_fiscal_with_company(branch_1 + branch_2)
|
||||
# Invoice fiscal test with branch
|
||||
with self._set_env_company(branch_1):
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_intra_state',
|
||||
partner=self.partner_b,
|
||||
taxes=self.sgst_sale_18,
|
||||
)
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_inter_state',
|
||||
partner=self.partner_a,
|
||||
taxes=self.igst_sale_18,
|
||||
)
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_export_sez_in',
|
||||
partner=self.partner_foreign,
|
||||
taxes=self.igst_sale_18,
|
||||
)
|
||||
|
||||
def test_l10n_in_fiscal_in_bill_to_ship_to(self):
|
||||
with self._set_env_company(self.default_company):
|
||||
# Inter State
|
||||
out_invoice = self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_inter_state',
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_sale_18,
|
||||
post=False,
|
||||
)
|
||||
# Intra State
|
||||
out_invoice.write({
|
||||
'l10n_in_state_id': self.env.ref('base.state_in_gj').id,
|
||||
})
|
||||
self.assertEqual(
|
||||
out_invoice.fiscal_position_id,
|
||||
self.env['account.chart.template'].ref('fiscal_position_in_intra_state')
|
||||
)
|
||||
# Outside India (Export/SEZ)
|
||||
out_invoice.write({
|
||||
'l10n_in_state_id': self.env.ref('l10n_in.state_in_oc').id, # Other Country State
|
||||
})
|
||||
self.assertEqual(
|
||||
out_invoice.fiscal_position_id,
|
||||
self.env['account.chart.template'].ref('fiscal_position_in_export_sez_in')
|
||||
)
|
||||
|
||||
def test_l10n_in_fiscal_in_vendor_bills(self):
|
||||
'''
|
||||
In Purchase Document: Compare place of supply with vendor state
|
||||
'''
|
||||
|
||||
with self._set_env_company(self.default_company):
|
||||
template = self.env['account.chart.template']
|
||||
company_state = self.env.company.state_id
|
||||
other_state = self.env['res.country.state'].search([
|
||||
('id', '!=', company_state.id),
|
||||
('country_id', '=', company_state.country_id.id)
|
||||
], limit=1)
|
||||
|
||||
# Sub-test: Intra-State
|
||||
with self.subTest(scenario="Intra-State"):
|
||||
self.partner_a.write({'state_id': company_state.id})
|
||||
vendor_bill = self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_intra_state',
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
post=False,
|
||||
)
|
||||
self.partner_a.write({'state_id': other_state.id})
|
||||
vendor_bill.write({'l10n_in_state_id': other_state.id})
|
||||
self.assertEqual(
|
||||
vendor_bill.fiscal_position_id,
|
||||
template.ref('fiscal_position_in_intra_state')
|
||||
)
|
||||
|
||||
# Sub-test: Inter-State
|
||||
with self.subTest(scenario="Inter-State"):
|
||||
self.partner_a.write({'state_id': other_state.id})
|
||||
vendor_bill = self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_inter_state',
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
post=False,
|
||||
)
|
||||
self.partner_a.write({'state_id': company_state.id})
|
||||
vendor_bill.write({'l10n_in_state_id': other_state.id})
|
||||
self.assertEqual(
|
||||
vendor_bill.fiscal_position_id,
|
||||
template.ref('fiscal_position_in_inter_state')
|
||||
)
|
||||
|
||||
# Sub-test: Export/SEZ (Outside India)
|
||||
with self.subTest(scenario="Export/SEZ"):
|
||||
vendor_bill = self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_export_sez_in',
|
||||
partner=self.partner_foreign,
|
||||
move_type='in_invoice',
|
||||
post=False,
|
||||
)
|
||||
vendor_bill.write({'l10n_in_state_id': self.env.ref('l10n_in.state_in_oc').id}) # Other Country State
|
||||
self.assertEqual(
|
||||
vendor_bill.fiscal_position_id,
|
||||
template.ref('fiscal_position_in_export_sez_in')
|
||||
)
|
||||
# Here fpos should Inter-State. But due to `l10n_in_gst_treatment` it will be Export/SEZ
|
||||
self.partner_a.write({'state_id': other_state.id})
|
||||
vendor_bill.write({
|
||||
'partner_id': self.partner_a.id, # Inter-State Partner
|
||||
'l10n_in_state_id': company_state.id, # Company State
|
||||
'l10n_in_gst_treatment': 'special_economic_zone',
|
||||
})
|
||||
self.assertEqual(
|
||||
vendor_bill.fiscal_position_id,
|
||||
template.ref('fiscal_position_in_sez')
|
||||
)
|
||||
|
||||
# Sub-test: Manual Partner Fiscal Check
|
||||
with self.subTest(scenario="Manual Partner Fiscal Check"):
|
||||
# Here fpos should be Intra-State. But due to `property_account_position_id` it will be Export/SEZ
|
||||
self.partner_a.write({
|
||||
'state_id': company_state.id, # Intra-State Partner
|
||||
'property_account_position_id': template.ref('fiscal_position_in_export_sez_in').id
|
||||
})
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_export_sez_in',
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
)
|
||||
|
||||
def test_l10n_in_company_with_no_vat(self):
|
||||
"""
|
||||
Test the company with no VAT and update the partner and company states as per the GSTIN number
|
||||
"""
|
||||
company = self.default_company
|
||||
|
||||
company.write({'vat': False})
|
||||
self.assertFalse(company.vat)
|
||||
company.action_update_state_as_per_gstin()
|
||||
self.assertEqual(company.partner_id.state_id, self.env.ref('base.state_in_gj'))
|
||||
|
||||
company.write({'vat': '36AABCT1332L011'})
|
||||
company.action_update_state_as_per_gstin()
|
||||
self.assertEqual(company.state_id, self.env.ref('base.state_in_ts'))
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
from odoo.tests import tagged
|
||||
import logging
|
||||
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestReports(L10nInTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.partner_b.l10n_in_gst_treatment = 'regular'
|
||||
cls.partner_a.l10n_in_gst_treatment = 'composition'
|
||||
cls.partner_foreign.l10n_in_gst_treatment = 'overseas'
|
||||
cls.partner_foreign_no_state.l10n_in_gst_treatment = 'overseas'
|
||||
|
||||
cls.igst_sale_18 = cls.env['account.chart.template'].ref('igst_sale_18')
|
||||
|
||||
def test_partner_details_change_with_invoice(self):
|
||||
invoice_b_2 = self.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=self.partner_b,
|
||||
amounts=[250, 600],
|
||||
taxes=[self.igst_sale_18],
|
||||
post=True
|
||||
)
|
||||
|
||||
# Place of Supply (pos) is same as of state of partner (for journal type sale)
|
||||
expected_pos_id = self.state_in_mh.id
|
||||
self.assertRecordValues(
|
||||
self.invoice_b,
|
||||
[{
|
||||
'state': 'draft',
|
||||
'l10n_in_gst_treatment': self.partner_b.l10n_in_gst_treatment,
|
||||
'l10n_in_state_id': expected_pos_id,
|
||||
}]
|
||||
)
|
||||
self.assertRecordValues(
|
||||
invoice_b_2,
|
||||
[{
|
||||
'state': 'posted',
|
||||
'l10n_in_gst_treatment': self.partner_b.l10n_in_gst_treatment,
|
||||
'l10n_in_state_id': expected_pos_id,
|
||||
}]
|
||||
)
|
||||
self.assertRecordValues(
|
||||
invoice_b_2,
|
||||
[{ # check gst treatment and pos doesn't change on posted invoice
|
||||
'state': 'posted',
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
}]
|
||||
)
|
||||
|
||||
def test_partner_change_with_invoice(self):
|
||||
in_invoice = self.init_invoice(
|
||||
move_type='in_invoice',
|
||||
partner=self.partner_b,
|
||||
amounts=[452, 58, 110],
|
||||
taxes=[self.igst_sale_18],
|
||||
)
|
||||
|
||||
self.invoice_a.partner_id = self.partner_foreign
|
||||
self.assertRecordValues(
|
||||
self.invoice_a,
|
||||
[{
|
||||
'state': 'draft',
|
||||
'l10n_in_gst_treatment': self.partner_foreign.l10n_in_gst_treatment,
|
||||
'l10n_in_state_id': self.env.ref("l10n_in.state_in_oc").id,
|
||||
}]
|
||||
)
|
||||
self.assertRecordValues(
|
||||
in_invoice,
|
||||
[{
|
||||
'state': 'draft',
|
||||
'l10n_in_gst_treatment': self.partner_b.l10n_in_gst_treatment,
|
||||
'l10n_in_state_id': self.env.company.state_id.id,
|
||||
}]
|
||||
)
|
||||
|
||||
def test_place_of_supply(self):
|
||||
child_partner = self.env['res.partner'].create({
|
||||
'name': "Child Contact",
|
||||
'type': "delivery",
|
||||
'parent_id': self.partner_a.id,
|
||||
'state_id': self.state_in_hp.id
|
||||
})
|
||||
|
||||
self.assertRecordValues(
|
||||
self.invoice_a,
|
||||
[{
|
||||
'partner_shipping_id': self.partner_a.id,
|
||||
'l10n_in_state_id': self.partner_a.state_id.id,
|
||||
}]
|
||||
)
|
||||
self.invoice_a.partner_shipping_id = child_partner
|
||||
self.assertRecordValues(
|
||||
self.invoice_a,
|
||||
[{
|
||||
'partner_shipping_id': child_partner.id,
|
||||
'l10n_in_state_id': child_partner.state_id.id,
|
||||
}]
|
||||
)
|
||||
self.invoice_a.partner_shipping_id = self.partner_b
|
||||
self.assertRecordValues(
|
||||
self.invoice_a,
|
||||
[{
|
||||
'l10n_in_state_id': self.partner_a.state_id.id,
|
||||
}]
|
||||
)
|
||||
|
||||
def test_foreign_customer_without_state(self):
|
||||
""" Verify foreign customer without state_id gets foreign state reference """
|
||||
self.assertRecordValues(
|
||||
self.invoice_d,
|
||||
[{
|
||||
'l10n_in_gst_treatment': 'overseas',
|
||||
'l10n_in_state_id': self.env.ref("l10n_in.state_in_oc").id,
|
||||
}]
|
||||
)
|
||||
|
||||
def test_government_gstin_extraction_tan(self):
|
||||
""" Verify that a GSTIN based on a TAN (Government entity) correctly populates the TAN field and leaves the PAN field empty. """
|
||||
gov_partner = self.env['res.partner'].create({
|
||||
'name': "Gov Partner",
|
||||
'country_id': self.env.ref('base.in').id,
|
||||
'state_id': self.env.ref('base.state_in_dl').id,
|
||||
'vat': '07DELN10357E1DH',
|
||||
})
|
||||
self.assertRecordValues(gov_partner, [{
|
||||
'l10n_in_pan_entity_id': False,
|
||||
'l10n_in_tan': 'DELN10357E',
|
||||
}])
|
||||
|
|
@ -0,0 +1,596 @@
|
|||
from odoo import Command
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestTdsTcsAlert(L10nInTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
ChartTemplate = cls.env['account.chart.template']
|
||||
|
||||
# ==== Chart of Accounts ====
|
||||
cls.purchase_account = ChartTemplate.ref('p2107')
|
||||
cls.purchase_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tds_section_194c').id
|
||||
})
|
||||
cls.house_expense_account = ChartTemplate.ref('p2103')
|
||||
cls.house_expense_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tds_section_194c').id
|
||||
})
|
||||
cls.internet_account = ChartTemplate.ref('p2105')
|
||||
cls.internet_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tds_section_194j').id
|
||||
})
|
||||
cls.rent_account = ChartTemplate.ref('p2111')
|
||||
cls.rent_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tds_section_194ib').id
|
||||
})
|
||||
cls.sale_account = ChartTemplate.ref('p20011')
|
||||
cls.sale_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tcs_section_206c1g_r').id
|
||||
})
|
||||
cls.service_account = ChartTemplate.ref('p20021')
|
||||
cls.creditors_account = ChartTemplate.ref('p11211')
|
||||
|
||||
# ==== Taxes ====
|
||||
cls.tax_194c = ChartTemplate.ref('tds_20_us_194c')
|
||||
cls.tax_194c.write({'l10n_in_section_id': cls.env.ref('l10n_in.tds_section_194c').id})
|
||||
cls.tax_194j = ChartTemplate.ref('tds_10_us_194j')
|
||||
cls.tax_194j.write({'l10n_in_section_id': cls.env.ref('l10n_in.tds_section_194j').id})
|
||||
cls.tax_194ib = ChartTemplate.ref('tds_20_us_194ib')
|
||||
cls.tax_194ib.write({'l10n_in_section_id': cls.env.ref('l10n_in.tds_section_194ib').id})
|
||||
cls.tax_206c1g_r = ChartTemplate.ref('tcs_5_us_206c_1g_som')
|
||||
cls.tax_206c1g_r.write({'l10n_in_section_id': cls.env.ref('l10n_in.tcs_section_206c1g_r').id})
|
||||
|
||||
country_in_id = cls.env.ref("base.in").id
|
||||
|
||||
cls.partner_b.write({
|
||||
'vat': '27ABCPM8965E1ZE',
|
||||
})
|
||||
cls.partner_foreign_2 = cls.partner_foreign.copy()
|
||||
|
||||
# ==== Company ====
|
||||
cls.env.company.write({
|
||||
'child_ids': [
|
||||
Command.create({
|
||||
'name': 'Branch A',
|
||||
"state_id": cls.env.ref("base.state_in_gj").id,
|
||||
'account_fiscal_country_id': country_in_id,
|
||||
'country_id': country_in_id,
|
||||
}),
|
||||
Command.create({
|
||||
'name': 'Branch B',
|
||||
"state_id": cls.env.ref("base.state_in_mh").id,
|
||||
'account_fiscal_country_id': country_in_id,
|
||||
'country_id': country_in_id,
|
||||
}),
|
||||
Command.create({
|
||||
'name': 'Branch C',
|
||||
"state_id": cls.env.ref("base.state_in_mp").id,
|
||||
'account_fiscal_country_id': country_in_id,
|
||||
'country_id': country_in_id,
|
||||
}),
|
||||
],
|
||||
})
|
||||
cls.cr.precommit.run() # load the CoA
|
||||
|
||||
cls.branch_a, cls.branch_b, cls.branch_c = cls.env.company.child_ids
|
||||
|
||||
def create_invoice(self, move_type=None, partner=None, invoice_date=None, amounts=None, taxes=[], company=None, accounts=[], quantities=[]):
|
||||
invoice = self.init_invoice(
|
||||
move_type=move_type or 'in_invoice',
|
||||
partner=partner,
|
||||
invoice_date=invoice_date,
|
||||
post=False,
|
||||
amounts=amounts,
|
||||
company=company
|
||||
)
|
||||
|
||||
for i, account in enumerate(accounts):
|
||||
invoice.invoice_line_ids[i].account_id = account
|
||||
|
||||
for i, quantity in enumerate(quantities):
|
||||
invoice.invoice_line_ids[i].quantity = quantity
|
||||
|
||||
for i, tax in enumerate(taxes):
|
||||
invoice.invoice_line_ids[i].tax_ids = tax
|
||||
invoice.action_post()
|
||||
return invoice
|
||||
|
||||
def tds_wizard_entry(self, move, lines):
|
||||
journal_id = self.env['account.journal'].search([('company_id', '=', self.env.company.id),('type', '=', 'general')], limit=1)
|
||||
for tax, amount in lines:
|
||||
self.env['l10n_in.withhold.wizard'].with_context(active_model='account.move', active_ids=move.ids).create({
|
||||
'journal_id': journal_id.id,
|
||||
'tax_id': tax.id,
|
||||
'base': amount,
|
||||
'date': move.invoice_date,
|
||||
}).action_create_and_post_withhold()
|
||||
|
||||
def reverse_move(self, move, date):
|
||||
move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=move.ids).create({
|
||||
'date': date,
|
||||
'reason': 'no reason',
|
||||
'journal_id': move.journal_id.id,
|
||||
})
|
||||
return move_reversal.refund_moves()
|
||||
|
||||
def test_tcs_tds_warning(self):
|
||||
'''
|
||||
Test that if any of the limit is not exceeded.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[29000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account],
|
||||
quantities=[1]
|
||||
)
|
||||
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_on_exceeded_per_transaction_limit(self):
|
||||
'''
|
||||
Test that if the per transaction limit is exceeded.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[31000],
|
||||
company=self.branch_a,
|
||||
quantities=[1]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[31000],
|
||||
company=self.branch_b,
|
||||
quantities=[1]
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[31000],
|
||||
company=self.branch_b,
|
||||
quantities=[1]
|
||||
)
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[31000],
|
||||
company=self.branch_b,
|
||||
quantities=[1]
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_on_monthly_aggregate_limit(self):
|
||||
'''
|
||||
Test the monthly aggregate limit, the warning
|
||||
message should be set accordingly.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-07-06',
|
||||
amounts=[20000],
|
||||
company=self.branch_b,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-07-16',
|
||||
amounts=[31000],
|
||||
company=self.branch_c,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194IB on this transaction.")
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-09-06',
|
||||
amounts=[50000],
|
||||
company=self.branch_c,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning, False)
|
||||
|
||||
move_4 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-09-16',
|
||||
amounts=[50000],
|
||||
company=self.branch_c,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move_4.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194IB on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_partner_wiht_pan(self):
|
||||
'''
|
||||
Test the aggregate limit when partner don't have
|
||||
pan number and having pan number.
|
||||
'''
|
||||
# no pan number
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_foreign,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_foreign_2,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_b,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
# same pan number
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning, False)
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_b,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194J on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_on_exceeded_aggregate_limit(self):
|
||||
'''
|
||||
Test that if the aggregate limit is exceeded.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[20000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-07-06',
|
||||
amounts=[20000],
|
||||
company=self.branch_b,
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-08-06',
|
||||
amounts=[31000],
|
||||
company=self.branch_c,
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-09-06',
|
||||
amounts=[5000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning, False)
|
||||
|
||||
move_4 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-10-07',
|
||||
amounts=[20000],
|
||||
company=self.branch_b,
|
||||
)
|
||||
self.assertEqual(move_4.l10n_in_warning, False)
|
||||
|
||||
move_5 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-11-08',
|
||||
amounts=[25000],
|
||||
company=self.branch_c,
|
||||
)
|
||||
self.assertEqual(move_5.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_on_case_of_credit_note(self):
|
||||
'''
|
||||
Test that the aggregate limit in case of debit/credit note.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-09-01',
|
||||
amounts=[2000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-09-01',
|
||||
amounts=[3000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.reverse_move(move, '2024-09-01')
|
||||
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-09-01',
|
||||
amounts=[2000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_cleared_on_available_tax(self):
|
||||
'''
|
||||
Test when a tax is added to the move line with a similar tax group
|
||||
as the account.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
move_type='out_invoice',
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[710000],
|
||||
taxes=[self.tax_206c1g_r],
|
||||
company=self.branch_a,
|
||||
)
|
||||
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_multiple_accounts_in_lines(self):
|
||||
'''
|
||||
Test when there are multiple products in the move line and some of them
|
||||
have different accounts which have the different tax group as the account.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[100000, 1100000, 710000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.rent_account, self.internet_account, self.purchase_account],
|
||||
quantities=[15, 16, 10]
|
||||
)
|
||||
self.assertTrue(move.l10n_in_warning)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[1000000.0, 1100000.0, 710000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.rent_account, self.internet_account, self.purchase_account],
|
||||
)
|
||||
self.tds_wizard_entry(move=move_1, lines=[(self.tax_194ib, 100000), (self.tax_194j, 100000), (self.tax_194c, 100000)])
|
||||
move_1.button_draft()
|
||||
move_1.action_post()
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_if_line_has_price_zero(self):
|
||||
'''
|
||||
Test when any invoice line has Zero
|
||||
'''
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[101000, 0],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[0],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_all_lines_do_not_have_taxes(self):
|
||||
'''
|
||||
Test when tds entry created and warning will removed
|
||||
'''
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[1000, 6000],
|
||||
company=self.branch_a,
|
||||
accounts=[],
|
||||
quantities=[15, 16]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
self.tds_wizard_entry(move=move, lines=[(self.tax_194c, 100000)])
|
||||
move.line_ids.remove_move_reconcile()
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_company_branches(self):
|
||||
'''
|
||||
Test when the aggregate limit is exceeded in case of multiple branches
|
||||
of the company,the warning message should be set accordingly.
|
||||
'''
|
||||
|
||||
self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-05-14',
|
||||
amounts=[25000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
|
||||
self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-05-14',
|
||||
amounts=[25000],
|
||||
company=self.branch_b,
|
||||
)
|
||||
|
||||
self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-05-14',
|
||||
amounts=[25000],
|
||||
company=self.branch_c,
|
||||
quantities=[25]
|
||||
)
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-05-14',
|
||||
amounts=[28000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_tcs_use_in_bill(self):
|
||||
'''
|
||||
Test when tcs section is used in the bill creation.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-05-29',
|
||||
amounts=[1100000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.sale_account]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_tds_use_in_invoice(self):
|
||||
'''
|
||||
Test when tcs section is used in the bill creation.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-05-29',
|
||||
amounts=[110000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_multiple_accounts_same_section_in_lines(self):
|
||||
'''
|
||||
Test when there are multiple products in the move line and some of them
|
||||
have different accounts which have the same tax group as the account.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[17000, 14000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.house_expense_account, self.purchase_account],
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[17000, 13000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.house_expense_account, self.purchase_account],
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[30000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.house_expense_account],
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning, False)
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[10000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_for_not_consider_draft_cancel_invoices_for_aggregate(self):
|
||||
'''
|
||||
Test to exclude draft and canceled invoices from aggregate
|
||||
total calculation.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[16000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.purchase_account],
|
||||
)
|
||||
move.button_cancel()
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[25000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.purchase_account],
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[85000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.purchase_account],
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_if_some_lines_has_tax(self):
|
||||
'''
|
||||
Test when a tax is added to the some of the move line
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
move_type='out_invoice',
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[710000, 710000],
|
||||
taxes=[self.tax_206c1g_r],
|
||||
company=self.branch_a,
|
||||
)
|
||||
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to collect TCS u/s 206C(1G) Remittance on this transaction.")
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="account_account_tds_tcs_view_form_inherit" model="ir.ui.view">
|
||||
<field name="name">account.account.tds.tcs.view.form.inherit</field>
|
||||
<field name="model">account.account</field>
|
||||
<field name="inherit_id" ref="account.view_account_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='tax_ids']" position="after">
|
||||
<field name="l10n_in_tds_tcs_section_id" invisible="company_fiscal_country_code != 'IN' or (not l10n_in_tds_feature_enabled and not l10n_in_tcs_feature_enabled)"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_account_tds_tcs_view_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">account.account.tds.tcs.view.list.inherit</field>
|
||||
<field name="model">account.account</field>
|
||||
<field name="inherit_id" ref="account.view_account_list"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='account_type']" position="after">
|
||||
<field name="l10n_in_tds_tcs_section_id" optional="hide"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -5,32 +5,116 @@
|
|||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet" position="before">
|
||||
<div class="alert alert-warning mt-1 mb-1" role="alert" invisible="not l10n_in_warning">
|
||||
<div>
|
||||
<field name="l10n_in_warning" widget="actionable_errors"/>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='ref']" position="after">
|
||||
<field name="country_code" invisible="1"/>
|
||||
<field name="l10n_in_journal_type" invisible="1"/>
|
||||
<field name="l10n_in_state_id" domain="[('country_id.code', '=', 'IN')]" options="{'no_create': True, 'no_open': True}"
|
||||
attrs="{'invisible': ['|', ('country_code', '!=', 'IN'), ('move_type', '=', 'entry')], 'required': [('country_code', '=', 'IN'), ('move_type', '!=', 'entry'), ('l10n_in_journal_type', 'in', ('sale', 'purchase'))], 'readonly': [('state', '!=', 'draft')]}"/>
|
||||
<field name="l10n_in_state_id" domain="[('country_id.code', '=', 'IN')]"
|
||||
options="{'no_create': True, 'no_open': True}"
|
||||
invisible="country_code != 'IN' or not l10n_in_is_gst_registered_enabled or move_type == 'entry'"
|
||||
readonly="state != 'draft'"
|
||||
required="country_code == 'IN' and l10n_in_is_gst_registered_enabled and state == 'draft' and move_type != 'entry' and l10n_in_journal_type in ('sale', 'purchase')"/>
|
||||
<field name="l10n_in_gst_treatment"
|
||||
attrs="{'invisible': ['|', ('country_code', '!=', 'IN'), ('move_type', '=', 'entry')], 'required': [('country_code', '=', 'IN'), ('move_type', '!=', 'entry')], 'readonly': [('state', '!=', 'draft')]}"/>
|
||||
invisible="country_code != 'IN' or not l10n_in_is_gst_registered_enabled or move_type == 'entry'"
|
||||
readonly="state != 'draft'"
|
||||
required="country_code == 'IN' and l10n_in_is_gst_registered_enabled and state == 'draft' and move_type != 'entry'"/>
|
||||
<label for="l10n_in_partner_gstin_status"
|
||||
invisible="not l10n_in_show_gstin_status"/>
|
||||
<div name="status_date_container"
|
||||
invisible="not l10n_in_show_gstin_status">
|
||||
<field name="l10n_in_partner_gstin_status" class="d-none"/>
|
||||
<span class="text-nowrap" readonly="1">
|
||||
<span invisible="not l10n_in_partner_gstin_status"
|
||||
class="oe_inline text-success">Active</span>
|
||||
<span invisible="not l10n_in_gstin_verified_date or l10n_in_partner_gstin_status"
|
||||
class="oe_inline text-danger">Inactive</span>
|
||||
<span class="text-muted oe_inline">
|
||||
<span invisible="l10n_in_gstin_verified_date">Not Checked</span>
|
||||
<span invisible="not l10n_in_gstin_verified_date" class="ps-3">Checked: </span>
|
||||
<field name="l10n_in_gstin_verified_date" class="oe_inline" widget="remaining_days"/>
|
||||
<button name="l10n_in_verify_partner_gstin_status"
|
||||
type="object" icon="fa-refresh"
|
||||
class="oe_link p-0 ps-3" title="Verify GSTIN status" />
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<field name="reversed_entry_id" domain="[('move_type', '=', 'out_invoice'), ('commercial_partner_id', '=', commercial_partner_id)]" readonly="state != 'draft'" invisible="move_type != 'out_refund' or country_code != 'IN'"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@id='other_tab']/group[@id='other_tab_group']" position="after">
|
||||
<group string="Export India" attrs="{'invisible': ['|', ('l10n_in_gst_treatment', 'not in', ['overseas', 'deemed_export']), ('move_type', 'not in', ['out_invoice', 'out_refund'])]}">
|
||||
<field name="l10n_in_shipping_bill_number"/>
|
||||
<field name="l10n_in_shipping_bill_date"/>
|
||||
<field name="l10n_in_shipping_port_code_id"/>
|
||||
<group string="Export India" invisible="l10n_in_gst_treatment not in ['overseas', 'deemed_export'] or move_type not in ['out_invoice', 'out_refund']">
|
||||
<field name="l10n_in_shipping_bill_number" readonly="state != 'draft'"/>
|
||||
<field name="l10n_in_shipping_bill_date" readonly="state != 'draft'"/>
|
||||
<field name="l10n_in_shipping_port_code_id" readonly="state != 'draft'"/>
|
||||
</group>
|
||||
<group string="Import India" attrs="{'invisible': ['|', ('l10n_in_gst_treatment', 'not in', ['overseas', 'special_economic_zone']), ('move_type', 'not in', ['in_invoice', 'in_refund'])]}">
|
||||
<field name="l10n_in_shipping_bill_number" string="Bill of Entry Number"/>
|
||||
<field name="l10n_in_shipping_bill_date" string="Bill of Entry Date"/>
|
||||
<field name="l10n_in_shipping_port_code_id"/>
|
||||
<group string="Import India" invisible="l10n_in_gst_treatment not in ['overseas', 'special_economic_zone'] or move_type not in ['in_invoice', 'in_refund']">
|
||||
<field name="l10n_in_shipping_bill_number" string="Bill of Entry Number" readonly="state != 'draft'"/>
|
||||
<field name="l10n_in_shipping_bill_date" string="Bill of Entry Date" readonly="state != 'draft'"/>
|
||||
<field name="l10n_in_shipping_port_code_id" readonly="state != 'draft'"/>
|
||||
</group>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<xpath expr="//field[@name='partner_shipping_id']" position="before">
|
||||
<field name="l10n_in_reseller_partner_id"
|
||||
groups="l10n_in.group_l10n_in_reseller"
|
||||
attrs="{'invisible': ['|', '|',('move_type', 'not in', ('out_invoice', 'out_refund')), ('country_code', '!=', 'IN'), ('move_type', '=', 'entry')]}"
|
||||
/>
|
||||
invisible="move_type not in ('out_invoice', 'out_refund') or country_code != 'IN' or move_type == 'entry'"
|
||||
readonly="state != 'draft'"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='invoice_line_ids']/list//field[@name='product_id']" position="after">
|
||||
<field name="l10n_in_hsn_code" optional="hide" column_invisible="parent.country_code != 'IN'"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='journal_line_ids' or @name='line_ids']/list//field[@name='name']" position="after">
|
||||
<field name="l10n_in_hsn_code" optional="hide" column_invisible="parent.country_code != 'IN'"/>
|
||||
</xpath>
|
||||
<xpath expr="//header" position="inside">
|
||||
<button name="%(l10n_in_withholding_entry_form_action)d" string="TDS Entry" type="action" class="btn btn-secondary float-end"
|
||||
invisible="country_code != 'IN' or l10n_in_tds_deduction == 'no' or not l10n_in_tds_feature_enabled or move_type not in ('out_invoice', 'in_invoice', 'out_refund', 'in_refund') or state != 'posted'"/>
|
||||
<button name="action_l10n_in_apply_higher_tax" string="Apply Higher TCS" type="object" class="btn btn-secondary float-end"
|
||||
invisible="not l10n_in_display_higher_tcs_button or not l10n_in_tcs_feature_enabled"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_l10n_in_withholding_entries"
|
||||
class="oe_stat_button"
|
||||
type="object"
|
||||
icon="fa-list-alt"
|
||||
invisible="not l10n_in_tds_feature_enabled or not l10n_in_withhold_move_ids">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_text">TDS</span>
|
||||
<span class="o_stat_value"><field name="l10n_in_total_withholding_amount"/></span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//notebook/page[@id='aml_tab']" position="before">
|
||||
<page name="withholding_tab" string="TDS Information" invisible="not l10n_in_tds_feature_enabled or not l10n_in_withholding_line_ids">
|
||||
<field name="l10n_in_withholding_line_ids" nolabel="1" colspan="4">
|
||||
<list editable="bottom" string="TDS Information">
|
||||
<field name="tax_ids" string="Tax" widget="many2many_tax_tags"/>
|
||||
<field name="price_subtotal" string="Base Amount" sum="Total"/>
|
||||
<field name="l10n_in_withhold_tax_amount" string="TDS Amount" sum="Total"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_line_list_l10n_in_withholding" model="ir.ui.view">
|
||||
<field name="name">account.move.line.list.l10n.in.withholding</field>
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Journal items" editable="top" create="0" default_group_by="l10n_in_tds_tcs_section_id">
|
||||
<field name="product_id" readonly="1"/>
|
||||
<field name="name" widget="section_and_note_text"/>
|
||||
<field name="account_id"/>
|
||||
<field name="tax_ids"
|
||||
widget="many2many_tax_tags"
|
||||
domain="[('type_tax_use', '=', context.get('default_tax_type_use'))]"
|
||||
column_invisible="context.get('move_type')"/>
|
||||
<field name="l10n_in_tds_tcs_section_id" string="Suggested Section"/>
|
||||
<field name="price_total"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@
|
|||
<field name="model">account.journal</field>
|
||||
<field name="inherit_id" ref="account.view_account_journal_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name='profit_account_id' position="attributes">
|
||||
<attribute name="attrs">{'invisible': ['|', '&', ('country_code', '!=', 'IN'), ('type', '!=', 'cash'), '&', ('country_code', '=', 'IN'), ('type', 'not in', ['bank', 'cash', 'sale', 'purchase'])]}</attribute>
|
||||
<field name="profit_account_id" position="attributes">
|
||||
<attribute name="invisible">country_code != 'IN' and type != 'cash' or country_code == 'IN' and type not in ['bank', 'cash', 'sale', 'purchase']</attribute>
|
||||
</field>
|
||||
<field name='loss_account_id' position="attributes">
|
||||
<attribute name="attrs">{'invisible': ['|', '&', ('country_code', '!=', 'IN'), ('type', '!=', 'cash'), '&', ('country_code', '=', 'IN'), ('type', 'not in', ['bank', 'cash', 'sale', 'purchase'])]}</attribute>
|
||||
</field>
|
||||
<field name="company_id" position="after">
|
||||
<field name="l10n_in_gstin_partner_id" context="{'show_vat':True}" options='{"no_create": True,"always_reload": True}' attrs="{'invisible': [('country_code', '!=', 'IN')]}"/>
|
||||
<field name="loss_account_id" position="attributes">
|
||||
<attribute name="invisible">country_code != 'IN' and type != 'cash' or country_code == 'IN' and type not in ['bank', 'cash', 'sale', 'purchase']</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_move_line_tree_hsn_l10n_in" model="ir.ui.view">
|
||||
<field name="name">account.move.line.list.l10n_in</field>
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Products" editable="top" create="0">
|
||||
<field name="move_id"
|
||||
optional="show"
|
||||
column_invisible="not context.get('send_and_print')"/>
|
||||
<field name="product_id" readonly="1"/>
|
||||
<field name="name" widget="section_and_note_text"/>
|
||||
<field name="l10n_in_hsn_code"
|
||||
optional="show"
|
||||
column_invisible="context.get('tax_validation') or context.get('restrict_negative_discount_line')"/>
|
||||
<field name="tax_ids"
|
||||
optional="show"
|
||||
widget="many2many_tax_tags" domain="[('type_tax_use', '=', 'sale')]"
|
||||
column_invisible="not context.get('tax_validation', False)"/>
|
||||
<field name="discount"
|
||||
optional="show"
|
||||
column_invisible="not context.get('restrict_negative_discount_line', False)"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_line_tree_l10n_in" model="ir.ui.view">
|
||||
<field name="name">account.move.line.list.l10n_in</field>
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="inherit_id" ref="account.view_move_line_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='credit']" position="after">
|
||||
<field name="l10n_in_gstr_section" optional="hide" readonly="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_account_payment_form_inherit_l10n_in_withholding" model="ir.ui.view">
|
||||
<field name="name">account.payment.form.inherit.l10n_in_withholding</field>
|
||||
<field name="model">account.payment</field>
|
||||
<field name="inherit_id" ref="account.view_account_payment_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//header" position="inside">
|
||||
<button name="%(l10n_in_withholding_entry_form_action)d" string="TDS Entry" type="action" class="btn btn-secondary float-end"
|
||||
invisible="country_code != 'IN' or not l10n_in_tds_feature_enabled or state not in ('in_process', 'paid') or is_reconciled"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_l10n_in_withholding_entries"
|
||||
class="oe_stat_button"
|
||||
type="object"
|
||||
icon="fa-list-alt"
|
||||
invisible="not l10n_in_tds_feature_enabled or not l10n_in_withhold_move_ids">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_text">TDS</span>
|
||||
<span class="o_stat_value"><field name="l10n_in_total_withholding_amount"/></span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -5,9 +5,16 @@
|
|||
<field name="model">account.tax</field>
|
||||
<field name="inherit_id" ref="account.view_tax_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="is_base_affected" position="after">
|
||||
<field name="l10n_in_reverse_charge" attrs="{'invisible':['|', ('amount_type','=', 'group'), ('country_code', '!=', 'IN')]}"/>
|
||||
<field name="tax_scope" position="after">
|
||||
<field name="l10n_in_tax_type" invisible="amount_type == 'group' or country_code != 'IN'"/>
|
||||
</field>
|
||||
<field name="is_base_affected" position="after">
|
||||
<field name="l10n_in_reverse_charge" invisible="amount_type == 'group' or country_code != 'IN' or l10n_in_tax_type != 'gst'"/>
|
||||
<field name="l10n_in_is_lut" invisible="amount_type == 'group' or country_code != 'IN' or l10n_in_tax_type != 'gst'"/>
|
||||
</field>
|
||||
<xpath expr="//field[@name='tax_group_id']" position="after">
|
||||
<field name="l10n_in_section_id" invisible="country_code != 'IN' or not (l10n_in_tds_feature_enabled and l10n_in_tcs_feature_enabled) or l10n_in_tax_type not in ['tcs', 'tds_sale', 'tds_purchase']"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="l10n_in_pan_entity_view_form" model="ir.ui.view">
|
||||
<field name="name">l10n_in.pan.entity.view.form</field>
|
||||
<field name="model">l10n_in.pan.entity</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="PAN Entity">
|
||||
<div class="alert alert-warning w-100 d-flex align-items-center gap-1" invisible="tds_deduction != 'lower' or message_attachment_count" role="alert">
|
||||
<span>Attaching a certificate would provide better clarity and understanding.</span>
|
||||
</div>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name" help="Permanent Account Number"/>
|
||||
<field name="type"/>
|
||||
<field name="partner_ids" widget="many2many_tags_avatar" options="{'no_create': True}"/>
|
||||
<field name="tds_deduction" required="1"/>
|
||||
<field name="tds_certificate"
|
||||
widget="binary"
|
||||
filename="tds_certificate_filename"
|
||||
options="{'accepted_file_extensions': '.pdf', 'allowed_mime_type' : 'application/pdf'}"
|
||||
/>
|
||||
<field name="tds_certificate_filename" invisible="1"/> <!-- Hidden field to store the filename of the uploaded certificate -->
|
||||
</group>
|
||||
<group>
|
||||
<field name="msme_type"/>
|
||||
<field name="msme_number" placeholder="e.g. UDYAM-XX-00-0000000"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="l10n_in_pan_entity_view_tree" model="ir.ui.view">
|
||||
<field name="name">l10n_in.pan.entity.view.tree</field>
|
||||
<field name="model">l10n_in.pan.entity</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="PAN Entity">
|
||||
<field name="name"/>
|
||||
<field name="type"/>
|
||||
<field name="partner_ids" widget="many2many_tags_avatar" options="{'no_create': True}"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="l10n_in_pan_entity_action" model="ir.actions.act_window">
|
||||
<field name="name">PAN Entity</field>
|
||||
<field name="res_model">l10n_in.pan.entity</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_l10n_in_pan_entity"
|
||||
name="PAN Entity"
|
||||
parent="account.account_transactions_menu"
|
||||
action="l10n_in_pan_entity_action"
|
||||
/>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="l10n_in_section_alert_view_tree" model="ir.ui.view">
|
||||
<field name="name">l10n_in.section.alert.view.list</field>
|
||||
<field name="model">l10n_in.section.alert</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Section">
|
||||
<field name="name"/>
|
||||
<field name="tax_source_type"/>
|
||||
<field name="consider_amount"/>
|
||||
<field name="per_transaction_limit"/>
|
||||
<field name="aggregate_limit"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="l10n_in_section_alert_view_form" model="ir.ui.view">
|
||||
<field name="name">l10n_in.section.alert.view.form</field>
|
||||
<field name="model">l10n_in.section.alert</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Section">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" string="Section Name"/>
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group class="w-50" string="Threshold limits">
|
||||
<field name="consider_amount"/>
|
||||
<label for="is_per_transaction_limit"/>
|
||||
<div>
|
||||
<field class="w-25" name="is_per_transaction_limit" widget="boolean_toggle"/>
|
||||
<field class="w-25 text-center oe_inline" name="per_transaction_limit" invisible="not is_per_transaction_limit"/>
|
||||
</div>
|
||||
<label for="is_aggregate_limit"/>
|
||||
<div>
|
||||
<field class="w-25" name="is_aggregate_limit" widget="boolean_toggle"/>
|
||||
<field class="w-25 text-center oe_inline" name="aggregate_limit" invisible="not is_aggregate_limit"/>
|
||||
<field class="w-25" name="aggregate_period" invisible="not is_aggregate_limit"/>
|
||||
</div>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="l10n_in_section_alert_action" model="ir.actions.act_window">
|
||||
<field name="name">Section</field>
|
||||
<field name="res_model">l10n_in.section.alert</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -20,14 +20,14 @@
|
|||
</record>
|
||||
|
||||
<record id="l10n_in_port_code_tree_view" model="ir.ui.view">
|
||||
<field name="name">l10n_in.port.code.tree</field>
|
||||
<field name="name">l10n_in.port.code.list</field>
|
||||
<field name="model">l10n_in.port.code</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="India Port Code">
|
||||
<list string="India Port Code">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="state_id"/>
|
||||
</tree>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
<search string="India Port Code">
|
||||
<field name="name" string="Port" filter_domain="['|',('name', 'ilike', self),('code', 'ilike', self)]"/>
|
||||
<field name="state_id"/>
|
||||
<group expand="0" string="Group By">
|
||||
<group>
|
||||
<filter string="State" name="state" domain="[]" context="{'group_by': 'state_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,13 @@
|
|||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet" position="before">
|
||||
<div class="alert alert-warning" role="alert" invisible="'IN' not in fiscal_country_codes or not l10n_in_hsn_warning">
|
||||
<field name="l10n_in_hsn_warning"/>
|
||||
</div>
|
||||
</xpath>
|
||||
<field name="categ_id" position="after">
|
||||
<field name="l10n_in_hsn_code"/>
|
||||
<field name="l10n_in_hsn_description"/>
|
||||
<field name="l10n_in_hsn_code" widget="l10n_in_hsn_autocomplete" invisible="'IN' not in fiscal_country_codes or not l10n_in_is_gst_registered_enabled"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,19 @@
|
|||
<odoo>
|
||||
<template id="l10n_in_report_invoice_document_inherit" inherit_id="account.report_invoice_document" primary="True">
|
||||
<xpath expr="//div[@name='shipping_address_block']" position="inside">
|
||||
<div t-if="o.company_id.account_fiscal_country_id.code == 'IN' and o.partner_shipping_id.vat">
|
||||
<div t-if="o.company_id.account_fiscal_country_id.code == 'IN' and o.company_id.l10n_in_is_gst_registered and o.partner_shipping_id.vat">
|
||||
GSTIN: <span t-field="o.partner_shipping_id.vat"/>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='partner_vat_address_same_as_shipping']" position="attributes">
|
||||
<attribute name="t-if" add="o.company_id.l10n_in_is_gst_registered" separator="and"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='partner_vat_address_not_same_as_shipping']" position="attributes">
|
||||
<attribute name="t-if" add="o.company_id.l10n_in_is_gst_registered" separator="and"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='partner_vat_no_shipping']" position="attributes">
|
||||
<attribute name="t-if" add="o.company_id.l10n_in_is_gst_registered" separator="and"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@name='address_not_same_as_shipping']//t[@t-set='address']" position="inside">
|
||||
<t t-call="l10n_in.place_of_supply"/>
|
||||
</xpath>
|
||||
|
|
@ -15,45 +24,110 @@
|
|||
<xpath expr="//div[@name='no_shipping']//t[@t-set='address']" position="inside">
|
||||
<t t-call="l10n_in.place_of_supply"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@t-if='not is_html_empty(o.narration)']" position="before">
|
||||
<t t-if="o.company_id.account_fiscal_country_id.code == 'IN'">
|
||||
<p id="total_in_words" class="mb16">
|
||||
<strong>Total (In Words): </strong>
|
||||
<span t-field="o.amount_total_words"/>
|
||||
</p>
|
||||
</t>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//table[@name='invoice_line_table']/thead/tr/th[1]" position="after">
|
||||
<t t-if="o.company_id.account_fiscal_country_id.code == 'IN'">
|
||||
<t t-if="o.company_id.account_fiscal_country_id.code == 'IN' and o.company_id.l10n_in_is_gst_registered">
|
||||
<th>HSN/SAC</th>
|
||||
</t>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//t[@name='account_invoice_line_accountable']/td[1]" position="after">
|
||||
<td t-if="o.company_id.account_fiscal_country_id.code == 'IN'">
|
||||
<span t-if="line.product_id.l10n_in_hsn_code" t-field="line.product_id.l10n_in_hsn_code"></span>
|
||||
<td t-if="o.company_id.account_fiscal_country_id.code == 'IN' and o.company_id.l10n_in_is_gst_registered">
|
||||
<span t-if="line.l10n_in_hsn_code" t-field="line.l10n_in_hsn_code"></span>
|
||||
</td>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//h2" position="replace" >
|
||||
<t t-if="o.company_id.account_fiscal_country_id.code == 'IN'">
|
||||
<h2>
|
||||
<span t-if="o.move_type == 'out_invoice' and o.state == 'posted'" t-field="o.journal_id.name"/>
|
||||
<span t-if="o.move_type == 'out_invoice' and o.state == 'draft'">Draft <span t-field="o.journal_id.name"/></span>
|
||||
<span t-if="o.move_type == 'out_invoice' and o.state == 'cancel'">Cancelled <span t-field="o.journal_id.name"/></span>
|
||||
<span t-if="o.move_type == 'out_refund'">Credit Note</span>
|
||||
<span t-if="o.move_type == 'in_refund'">Vendor Credit Note</span>
|
||||
<span t-if="o.move_type == 'in_invoice'">Vendor Bill</span>
|
||||
<span t-field="o.name"/>
|
||||
</h2>
|
||||
<xpath expr="//t[@name='account_invoice_line_accountable']/following-sibling::*[1]/td[1]" position="after">
|
||||
<td t-if="o.company_id.account_fiscal_country_id.code == 'IN' and o.company_id.l10n_in_is_gst_registered"></td>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div[@id='qrcode_info']" position="attributes">
|
||||
<attribute name="t-if" add="o.company_id.account_fiscal_country_id.code != 'IN'" separator="and"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div[@id='qrcode_info']" position="after">
|
||||
<t t-if="o.company_id.account_fiscal_country_id.code == 'IN' and o.company_id.l10n_in_upi_id">
|
||||
<div style="display:-webkit-flex;" class="flex-column">
|
||||
<strong>PAYMENT QR CODE</strong>
|
||||
<div class="mt-1 mb-1">
|
||||
<p class="mb-0">UPI ID:</p>
|
||||
<span class="mb-0" t-field="o.company_id.l10n_in_upi_id"/>
|
||||
</div>
|
||||
<div class="d-flex flex-row" t-attf-style="#{'-webkit-transform:translateX(-0.5rem);' if report_type != 'html' else '-webkit-transform:translate(-0.5rem,-0.8rem);'}">
|
||||
<img src="/l10n_in/static/src/img/PhonePe-Logo.svg" style="width:4.5rem;"/>
|
||||
<img src="/l10n_in/static/src/img/Google_Pay-Logo.svg" style="width:3.5rem;"/>
|
||||
<img src="/l10n_in/static/src/img/Paytm-Logo.svg" style="width:4rem;"/>
|
||||
<img src="/l10n_in/static/src/img/Upi-logo.svg" t-attf-style="#{'' if report_type != 'html' else 'padding:0.5rem;'} width:4rem;"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
|
||||
<t name="invoice_title" position="replace">
|
||||
<t name="invoice_title">
|
||||
<span t-out="o._get_l10n_in_invoice_label()"/>
|
||||
</t>
|
||||
</t>
|
||||
<t name="proforma_invoice_title" position="replace">
|
||||
<t name="proforma_invoice_title">
|
||||
Proforma <span t-out="o._get_l10n_in_invoice_label()"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<xpath expr="//div[@id='payment_term']" position="after">
|
||||
<t t-if="o.company_id.account_fiscal_country_id.code == 'IN' and o.company_id.l10n_in_is_gst_registered">
|
||||
<t t-set="hsn_summary" t-value="o._l10n_in_get_hsn_summary_table()"/>
|
||||
<t t-if="hsn_summary">
|
||||
<div name="l10n_in_hsn_summary" class="mt-3" style="page-break-inside: avoid;">
|
||||
<h4>HSN Summary</h4>
|
||||
<table class="table table-sm table-borderless col-6">
|
||||
<thead>
|
||||
<th>HSN/SAC</th>
|
||||
<th class="text-end">Quantity</th>
|
||||
<th class="text-end">Rate %</th>
|
||||
<th class="text-end">Taxable Value</th>
|
||||
<th class="text-end" t-if="hsn_summary['has_gst']">SGST/UTGST</th>
|
||||
<th class="text-end" t-if="hsn_summary['has_gst']">CGST</th>
|
||||
<th class="text-end" t-if="hsn_summary['has_igst']">IGST</th>
|
||||
<th class="text-end" t-if="hsn_summary['has_cess']">CESS</th>
|
||||
</thead>
|
||||
<tr t-foreach="hsn_summary['items']" t-as="item">
|
||||
<td t-esc="item['l10n_in_hsn_code']"/>
|
||||
<td class="text-end">
|
||||
<span t-esc="item['quantity']"/>
|
||||
<span t-if="hsn_summary['display_uom']">(<t t-esc="item['uom_name']"/>)</span>
|
||||
</td>
|
||||
<td class="text-end" t-esc="item['rate']"/>
|
||||
<td class="text-end">
|
||||
<span t-esc="item['amount_untaxed']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end" t-if="hsn_summary['has_gst']">
|
||||
<span t-esc="item['tax_amount_sgst']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end" t-if="hsn_summary['has_gst']">
|
||||
<span t-esc="item['tax_amount_cgst']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end" t-if="hsn_summary['has_igst']">
|
||||
<span t-esc="item['tax_amount_igst']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end" t-if="hsn_summary['has_cess']">
|
||||
<span t-esc="item['tax_amount_cess']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<t t-else="">$0</t>
|
||||
</xpath>
|
||||
|
||||
</template>
|
||||
|
||||
<!-- Workarounds for Studio reports, see odoo/odoo#60660 -->
|
||||
<!-- Workaround for Studio reports, see odoo/odoo#60660 -->
|
||||
<template id="report_invoice" inherit_id="account.report_invoice">
|
||||
<xpath expr='//t[@t-call="account.report_invoice_document"]' position="after">
|
||||
<t t-elif="o._get_name_invoice_report() == 'l10n_in.l10n_in_report_invoice_document_inherit'"
|
||||
|
|
@ -62,16 +136,8 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="report_invoice_with_payments" inherit_id="account.report_invoice_with_payments">
|
||||
<xpath expr='//t[@t-call="account.report_invoice_document"]' position="after">
|
||||
<t t-elif="o._get_name_invoice_report() == 'l10n_in.l10n_in_report_invoice_document_inherit'"
|
||||
t-call="l10n_in.l10n_in_report_invoice_document_inherit"
|
||||
t-lang="lang"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="place_of_supply">
|
||||
<div t-if="o.l10n_in_state_id">
|
||||
<div t-if="o.company_id.l10n_in_is_gst_registered and o.l10n_in_state_id">
|
||||
Place of supply: <span t-out="o.l10n_in_state_id.name" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- get vat from journal_id for all layout -->
|
||||
<template id="l10n_in_external_layout" inherit_id="web.external_layout">
|
||||
<xpath expr="//t[@t-if='company.external_report_layout_id']" position="before">
|
||||
<t t-if="o and 'journal_id' in o and company.country_id.code == 'IN' and o.journal_id.l10n_in_gstin_partner_id.vat">
|
||||
<t t-set="forced_vat" t-value="o.journal_id.l10n_in_gstin_partner_id.vat"/>
|
||||
</t>
|
||||
<t t-elif="o and 'l10n_in_journal_id' in o and company.country_id.code == 'IN' and o.l10n_in_journal_id.l10n_in_gstin_partner_id.vat">
|
||||
<t t-set="forced_vat" t-value="o.l10n_in_journal_id.l10n_in_gstin_partner_id.vat"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_company_form" model="ir.ui.view">
|
||||
<field name="name">res.company.form.inherit.l10n_in_upi</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='currency_id']" position="after">
|
||||
<field name="l10n_in_upi_id" invisible="country_code != 'IN'"/>
|
||||
</xpath>
|
||||
<xpath expr="//label[@for='name']" position="attributes">
|
||||
<attribute name="invisible">l10n_in_pan_type</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//label[@for='name']" position="before">
|
||||
<field class="o_form_label mb-0" name="l10n_in_pan_type" invisible="not l10n_in_pan_type"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='vat']" position="after">
|
||||
<field name="l10n_in_pan_entity_id" invisible="country_code != 'IN'"/>
|
||||
<field name="l10n_in_tan" invisible="not l10n_in_pan_entity_id"/>
|
||||
</xpath>
|
||||
<xpath expr="//sheet" position="before">
|
||||
<div class="alert alert-warning mt-1 mb-1" role="alert" invisible="not l10n_in_gst_state_warning or country_code != 'IN'">
|
||||
<field name="l10n_in_gst_state_warning"/>
|
||||
<a name="action_update_state_as_per_gstin"
|
||||
string="update it"
|
||||
class="ms-1"
|
||||
invisible="country_code != 'IN'"
|
||||
type="object"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -5,22 +5,150 @@
|
|||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div id="invoicing_settings" position="inside">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box"
|
||||
name="ecommerce_reseller_setting"
|
||||
title="Manage Reseller(E-Commerce)"
|
||||
attrs="{'invisible': [('country_code', '!=', 'IN')]}">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="group_l10n_in_reseller"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane" name="l10n_eu_service_right_pane">
|
||||
<label for="group_l10n_in_reseller"/>
|
||||
<div class="text-muted">
|
||||
Use this if setup with Reseller(E-Commerce).
|
||||
<block id="invoicing_settings" position="inside">
|
||||
<setting help="Use this if setup with Reseller(E-Commerce)." name="ecommerce_reseller_setting" title="Manage Reseller(E-Commerce)" invisible="country_code != 'IN'">
|
||||
<field name="group_l10n_in_reseller"/>
|
||||
</setting>
|
||||
</block>
|
||||
<xpath expr="//block[@name='fiscal_localization_setting_container']" position="after">
|
||||
<div id="india_integration_section">
|
||||
<block title="Indian Integration" id="india_localization" invisible="country_code != 'IN'">
|
||||
<setting name="l10n_in_tds"
|
||||
help="Enable this to activate the Tax Deducted at Source (TDS) feature and the account-based TDS section suggestion."
|
||||
company_dependent="1"
|
||||
string="TDS"
|
||||
documentation="/applications/finance/fiscal_localizations/india.html">
|
||||
<field name="l10n_in_tds_feature"/>
|
||||
<div class="content-group" invisible="not l10n_in_tds_feature">
|
||||
<div class="row mt8">
|
||||
<label for="l10n_in_tan" class="col-lg-5 o_light_label" string="TAN"/>
|
||||
<field name="l10n_in_tan" required="l10n_in_tds_feature"/>
|
||||
</div>
|
||||
</div>
|
||||
</setting>
|
||||
<setting name="l10n_in_tcs"
|
||||
help="Enable this to activate the Tax Collected at Source (TCS) feature and the account-based TCS section suggestion."
|
||||
company_dependent="1"
|
||||
string="TCS"
|
||||
documentation="/applications/finance/fiscal_localizations/india.html">
|
||||
<field name="l10n_in_tcs_feature"/>
|
||||
</setting>
|
||||
<setting string="Registered Under GST"
|
||||
company_dependent="1"
|
||||
help="Enable this if you have GST number except composition scheme.">
|
||||
<field name="l10n_in_is_gst_registered"/>
|
||||
<div invisible="not l10n_in_is_gst_registered">
|
||||
<div id="india_gst_number" class="row">
|
||||
<label string="GST Number" for="l10n_in_gstin" class="col-lg-4 o_light_label"/>
|
||||
<field name="l10n_in_gstin" required="l10n_in_is_gst_registered"/>
|
||||
</div>
|
||||
</div>
|
||||
</setting>
|
||||
<t invisible="not l10n_in_is_gst_registered">
|
||||
<setting id="gsp_selection" string="GSP" help="Select BVM as GSP Tera is Deprecated" documentation="/applications/finance/fiscal_localizations/india.html">
|
||||
<div class="text-muted">
|
||||
Changes to this configuration will be automatically applied to all Indian companies.
|
||||
Please refer to the <a href="https://www.odoo.com/documentation/16.0/applications/finance/fiscal_localizations/india.html#nic-e-invoice-registration">documentation</a> for detailed guidance on accurate configuration.
|
||||
<div class="text-warning" invisible="l10n_in_gsp == 'bvm'">
|
||||
The current GSP is scheduled for deprecation soon.
|
||||
We strongly recommend switching to BVM GSP at the earliest to ensure uninterrupted service.
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="mt16 row">
|
||||
<field name="l10n_in_gsp" nolabel="1"/>
|
||||
</div>
|
||||
</div>
|
||||
</setting>
|
||||
<setting name="india_production_setting"
|
||||
company_dependent="1"
|
||||
string="Production Environment"
|
||||
help="Activate this to start using Indian services in the production environment."
|
||||
groups="base.group_no_one">
|
||||
<field name="l10n_in_edi_production_env"/>
|
||||
<div class='mt8'
|
||||
invisible="not l10n_in_edi_production_env or (not module_l10n_in_edi and not l10n_in_gstin_status_feature and not module_l10n_in_ewaybill and not l10n_in_gst_efiling_feature)">
|
||||
<button name="l10n_in_edi_buy_iap"
|
||||
title="Costs 1 credit per transaction. Free 200 credits will be available for the first time."
|
||||
icon="fa-arrow-right"
|
||||
type="object"
|
||||
string="Buy credits"
|
||||
class="btn-link"/>
|
||||
</div>
|
||||
</setting>
|
||||
<setting name="electronic_invoices_in"
|
||||
help="Connect to NIC (National Informatics Center) to submit invoices on posting."
|
||||
company_dependent="1"
|
||||
string="E-Invoicing"
|
||||
documentation="/applications/finance/fiscal_localizations/india.html#india-e-invoicing">
|
||||
<field name="module_l10n_in_edi"/>
|
||||
<div class="content-group" invisible="not module_l10n_in_edi">
|
||||
<div class="text-warning mt16 mb4">
|
||||
Save this page and come back here to set up the feature.
|
||||
</div>
|
||||
</div>
|
||||
</setting>
|
||||
<setting name="electronic_waybill_in"
|
||||
help="Connect to NIC (National Informatics Center) to submit e-waybill on posting."
|
||||
company_dependent="1"
|
||||
string="E-Way bill"
|
||||
documentation="/applications/finance/fiscal_localizations/india.html#india-e-waybill">
|
||||
<field name="module_l10n_in_ewaybill" class="oe_inline"/>
|
||||
<div class="content-group" invisible="not module_l10n_in_ewaybill">
|
||||
<div class="text-warning mt16 mb4">
|
||||
Save this page and come back here to set up the feature.
|
||||
</div>
|
||||
</div>
|
||||
</setting>
|
||||
<setting name="india_gstr"
|
||||
help="Connect to GST portal to submit GSTR-1 and get GSTR-2B"
|
||||
company_dependent="1"
|
||||
documentation="/applications/finance/fiscal_localizations/india.html#india-gstr">
|
||||
<field name="l10n_in_gst_efiling_feature" widget="upgrade_boolean"/>
|
||||
</setting>
|
||||
<setting name="india_gstin_status_api_settings"
|
||||
string="Check GST Number Status"
|
||||
company_dependent="1"
|
||||
help="Enable this to check the GST Number status"
|
||||
documentation="/applications/finance/fiscal_localizations/india.html#indian-check-gstin-status">
|
||||
<field name="l10n_in_gstin_status_feature" class="oe_inline"/>
|
||||
</setting>
|
||||
<setting name="india_gstr_api"
|
||||
help="This feature helps you to quickly create draft vendor bills using the data your vendor submitted during E-invoicing on the GST portal."
|
||||
company_dependent="1">
|
||||
<field name="l10n_in_fetch_vendor_edi_feature" widget="upgrade_boolean"/>
|
||||
</setting>
|
||||
<setting string="HSN/SAC Validation"
|
||||
help="HSN/SAC Digit Validation for GST Compliance based on your Aggregate Annual Turnover (AATO)."
|
||||
company_dependent="1">
|
||||
<field name="l10n_in_hsn_code_digit"/>
|
||||
</setting>
|
||||
</t>
|
||||
</block>
|
||||
</div>
|
||||
</xpath>
|
||||
<block id="print_vendor_checks_setting_container" position="inside">
|
||||
<setting id="vendor_payment_order_file"
|
||||
company_dependent="1"
|
||||
help="Generate Vendor Payment Order file(csv file), upload to your bank to make the payments"
|
||||
invisible="country_code != 'IN'">
|
||||
<field name="l10n_in_enet_vendor_batch_payment_feature" widget="upgrade_boolean"/>
|
||||
</setting>
|
||||
</block>
|
||||
<block id="default_accounts" position="inside">
|
||||
<setting string="India TDS Control:" invisible="not l10n_in_tds_feature">
|
||||
<div class="content-group">
|
||||
<div class="row mt8">
|
||||
<label for="l10n_in_withholding_journal_id" class="col-lg-5 o_light_label" string="Journal"/>
|
||||
<field name="l10n_in_withholding_journal_id" domain="[('type', '=', 'general')]"/>
|
||||
</div>
|
||||
<div class="row mt8">
|
||||
<label for="l10n_in_withholding_account_id" class="col-lg-5 o_light_label" string="Account"/>
|
||||
<field name="l10n_in_withholding_account_id"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</setting>
|
||||
</block>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,18 @@
|
|||
<field name="inherit_id" ref="base.view_country_state_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="code" position="after">
|
||||
<field name="l10n_in_tin" attrs="{'invisible': [('country_id', '!=', %(base.in)d)]}"/>
|
||||
<field name="l10n_in_tin" invisible="country_id != %(base.in)d"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="l10n_in_view_country_state_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">l10n.in.res.country.state.list.inhert</field>
|
||||
<field name="model">res.country.state</field>
|
||||
<field name="inherit_id" ref="base.view_country_state_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="code" position="after">
|
||||
<field name="l10n_in_tin" groups="base.group_no_one" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
|||
|
|
@ -4,13 +4,82 @@
|
|||
<field name="name">l10n.in.res.partner.vat.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="priority" eval="90"/>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="inherit_id" ref="account.view_partner_property_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='vat']" position="attributes">
|
||||
<attribute name="attrs">{'required':[('l10n_in_gst_treatment', 'in', ['regular', 'composition', 'special_economic_zone', 'deemed_export'])], 'readonly': [('parent_id', '!=', False)]}</attribute>
|
||||
<attribute name="required">l10n_in_is_gst_registered_enabled and l10n_in_gst_treatment in ['regular', 'composition', 'special_economic_zone', 'deemed_export']</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='vat']" position="before">
|
||||
<field name="l10n_in_gst_treatment" attrs="{'readonly': [('parent_id', '!=', False)]}"/>
|
||||
<field name="l10n_in_gst_treatment" invisible="'IN' not in fiscal_country_codes or not l10n_in_is_gst_registered_enabled" readonly="parent_id"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='vat']" position="after">
|
||||
<field name="l10n_in_pan_entity_id" invisible="country_code != 'IN'" readonly="parent_id"/>
|
||||
<field name="l10n_in_tan" invisible="country_code != 'IN' or not l10n_in_pan_entity_id" readonly="parent_id"/>
|
||||
</xpath>
|
||||
<xpath expr="//sheet" position="before">
|
||||
<div class="alert alert-warning" role="alert"
|
||||
invisible="not display_pan_warning">
|
||||
PAN number is not same as the 3rd to 12th characters of the GST number.
|
||||
</div>
|
||||
<div class="alert alert-warning mt-1 mb-1" role="alert" invisible="not l10n_in_gst_state_warning or country_code != 'IN'">
|
||||
<field name="l10n_in_gst_state_warning"/>
|
||||
<a name="action_update_state_as_per_gstin"
|
||||
string="update it"
|
||||
class="ms-1"
|
||||
invisible="country_code != 'IN'"
|
||||
type="object"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="l10n_in_view_partner_tree" model="ir.ui.view">
|
||||
<field name="name">l10n.in.res.partner.tree</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='vat']" position="after">
|
||||
<field name="l10n_in_pan_entity_id" optional="hide"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="l10n_in_view_res_partner_filter" model="ir.ui.view">
|
||||
<field name="name">l10n.in.view.res.partner.filter.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_res_partner_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="user_id" position="after">
|
||||
<field name="l10n_in_pan_entity_id"/>
|
||||
</field>
|
||||
<filter name="group_country" position="after">
|
||||
<filter name="l10n_in_pan_entity_id" string="PAN" context="{'group_by': 'l10n_in_pan_entity_id'}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="l10n_in_view_partner_base_vat_form" model="ir.ui.view">
|
||||
<field name="name">l10n.in.gstin.status.view.partner.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base_vat.view_partner_base_vat_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='vies_valid']" position="after">
|
||||
<span invisible="country_code != 'IN' or not l10n_in_gstin_status_feature_enabled or not vat or 'IN' not in fiscal_country_codes">
|
||||
<span invisible="not l10n_in_gstin_verified_date or not l10n_in_gstin_verified_status"
|
||||
class="oe_inline text-success">Active</span>
|
||||
<span invisible="not l10n_in_gstin_verified_date or l10n_in_gstin_verified_status"
|
||||
class="oe_inline text-danger">Inactive</span>
|
||||
<span invisible="not l10n_in_gstin_verified_date and not l10n_in_gstin_verified_status" class="text-muted">
|
||||
(
|
||||
<field name="l10n_in_gstin_verified_date" widget="remaining_days" class="oe_inline" readonly="1" />
|
||||
<button name="action_l10n_in_verify_gstin_status" type="object" icon="fa-refresh"
|
||||
class="oe_link p-0 ps-2" title="Reverify GSTIN status" />
|
||||
)
|
||||
</span>
|
||||
<button string="Check Status" name="action_l10n_in_verify_gstin_status" type="object"
|
||||
icon="fa-check" class="oe_link p-0" title="Check GSTIN status"
|
||||
invisible="l10n_in_gstin_verified_date" />
|
||||
</span>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
|||
|
|
@ -5,19 +5,8 @@
|
|||
<field name="model">uom.uom</field>
|
||||
<field name="inherit_id" ref="uom.product_uom_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='category_id']" position="after">
|
||||
<field name="l10n_in_code"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_uom_categ_form_view_inherit_l10n_in" model="ir.ui.view">
|
||||
<field name="name">uom.category.form</field>
|
||||
<field name="model">uom.category</field>
|
||||
<field name="inherit_id" ref="uom.product_uom_categ_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='uom_ids']/tree/field[@name='name']" position="after">
|
||||
<field name="l10n_in_code"/>
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<field name="l10n_in_code" invisible="'IN' not in fiscal_country_codes"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
|||
1
odoo-bringout-oca-ocb-l10n_in/l10n_in/wizard/__init__.py
Normal file
1
odoo-bringout-oca-ocb-l10n_in/l10n_in/wizard/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import l10n_in_withhold_wizard
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
from markupsafe import Markup
|
||||
|
||||
from odoo import _, api, Command, fields, models
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
|
||||
class L10n_InWithholdWizard(models.TransientModel):
|
||||
_name = 'l10n_in.withhold.wizard'
|
||||
_description = "Withhold Wizard"
|
||||
_check_company_auto = True
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
result = super().default_get(fields)
|
||||
active_model = self.env.context.get('active_model')
|
||||
active_ids = self.env.context.get('active_ids', [])
|
||||
if active_model not in ('account.move', 'account.payment') or not active_ids:
|
||||
raise UserError(_("TDS must be created from an Invoice or a Payment."))
|
||||
if len(active_ids) > 1:
|
||||
raise UserError(_("You can only create a withhold for only one record at a time."))
|
||||
active_record = self.env[active_model].browse(active_ids)
|
||||
result['reference'] = _("TDS of %s", active_record.name)
|
||||
if active_model == 'account.move':
|
||||
if active_record.move_type not in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund') or active_record.state != 'posted':
|
||||
raise UserError(_("TDS must be created from Posted Customer Invoices, Customer Credit Notes, Vendor Bills or Vendor Refunds."))
|
||||
result['related_move_id'] = active_record.id
|
||||
elif active_model == 'account.payment':
|
||||
if not active_record.partner_id:
|
||||
type_name = _("Vendor Payment") if active_record.partner_type == 'supplier' else _("Customer Payment")
|
||||
raise UserError(_("Please set a partner on the %s before creating a withhold.", type_name))
|
||||
result['related_payment_id'] = active_record.id
|
||||
return result
|
||||
|
||||
reference = fields.Char(string="Reference")
|
||||
type_name = fields.Char(string="Type", compute='_compute_type_name')
|
||||
related_move_id = fields.Many2one(
|
||||
comodel_name='account.move',
|
||||
string="Invoice/Bill",
|
||||
readonly=True,
|
||||
)
|
||||
related_payment_id = fields.Many2one(
|
||||
comodel_name='account.payment',
|
||||
string="Payment",
|
||||
readonly=True,
|
||||
)
|
||||
tds_deduction = fields.Selection(
|
||||
selection=[
|
||||
('normal', 'Normal Deduction'),
|
||||
('lower', 'Lower Deduction'),
|
||||
('higher', 'Higher Deduction'),
|
||||
('no', 'No Deduction'),
|
||||
],
|
||||
string="TDS Deduction",
|
||||
compute='_compute_tds_deduction',
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name='res.company',
|
||||
string="Company",
|
||||
compute='_compute_company_id'
|
||||
)
|
||||
currency_id = fields.Many2one(
|
||||
related='company_id.currency_id',
|
||||
string="Currency",
|
||||
)
|
||||
journal_id = fields.Many2one(
|
||||
comodel_name='account.journal',
|
||||
string="Journal",
|
||||
compute='_compute_journal', precompute=True,
|
||||
readonly=False, store=True,
|
||||
required=True,
|
||||
check_company=True,
|
||||
)
|
||||
date = fields.Date(
|
||||
string="Date",
|
||||
default=fields.Date.context_today,
|
||||
)
|
||||
l10n_in_tds_tax_type = fields.Char(
|
||||
string="Indian Tax Type",
|
||||
compute='_compute_l10n_in_tds_tax_type'
|
||||
)
|
||||
l10n_in_withholding_warning = fields.Json(string="Withholding warning", compute='_compute_l10n_in_withholding_warning')
|
||||
base = fields.Monetary(string="Base Amount", compute='_compute_base', store=True, readonly=False)
|
||||
tax_id = fields.Many2one(
|
||||
comodel_name='account.tax',
|
||||
string="TDS Section",
|
||||
required=True,
|
||||
compute='_compute_tax_id',
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
amount = fields.Monetary(
|
||||
string="TDS Amount",
|
||||
compute='_compute_amount',
|
||||
)
|
||||
|
||||
# ===== Constraints =====
|
||||
@api.constrains('base')
|
||||
def _check_amounts(self):
|
||||
for wizard in self:
|
||||
if wizard.currency_id.compare_amounts(wizard.base, 0.0) <= 0:
|
||||
raise ValidationError(_("Negative or zero values are not allowed in Base Amount for withhold"))
|
||||
if wizard.currency_id.compare_amounts(wizard.amount, 0.0) <= 0:
|
||||
raise ValidationError(_("Negative or zero values are not allowed in TDS Amount for withhold"))
|
||||
|
||||
# ===== Computes =====
|
||||
@api.depends('related_move_id', 'related_payment_id')
|
||||
def _compute_l10n_in_tds_tax_type(self):
|
||||
for wizard in self:
|
||||
withhold_type = wizard._get_withhold_type()
|
||||
l10n_in_tds_tax_type = False
|
||||
if withhold_type in ('in_withhold', 'in_refund_withhold'):
|
||||
l10n_in_tds_tax_type = 'tds_purchase'
|
||||
elif withhold_type in ('out_withhold', 'out_refund_withhold'):
|
||||
l10n_in_tds_tax_type = 'tds_sale'
|
||||
wizard.l10n_in_tds_tax_type = l10n_in_tds_tax_type
|
||||
|
||||
@api.depends('related_move_id', 'related_payment_id')
|
||||
def _compute_tds_deduction(self):
|
||||
for wizard in self:
|
||||
related_partner = wizard.related_move_id.commercial_partner_id if wizard.related_move_id else wizard.related_payment_id.partner_id.commercial_partner_id
|
||||
wizard.tds_deduction = related_partner.l10n_in_pan_entity_id.tds_deduction if related_partner and related_partner.l10n_in_pan_entity_id else 'higher'
|
||||
|
||||
@api.depends('related_move_id', 'related_payment_id')
|
||||
def _compute_type_name(self):
|
||||
for wizard in self:
|
||||
if wizard.related_payment_id:
|
||||
wizard.type_name = _("Vendor Payment") if wizard.related_payment_id.partner_type == 'supplier' else _("Customer Payment")
|
||||
else:
|
||||
wizard.type_name = wizard.related_move_id.type_name
|
||||
|
||||
@api.depends('related_move_id', 'related_payment_id')
|
||||
def _compute_company_id(self):
|
||||
for wizard in self:
|
||||
wizard.company_id = wizard.related_move_id.company_id or wizard.related_payment_id.company_id
|
||||
|
||||
@api.depends('company_id')
|
||||
def _compute_journal(self):
|
||||
for wizard in self:
|
||||
wizard.journal_id = wizard.company_id.parent_ids.l10n_in_withholding_journal_id[-1:] or \
|
||||
wizard.env['account.journal'].search([*self.env['account.journal']._check_company_domain(wizard.company_id), ('type', '=', 'general')], limit=1)
|
||||
|
||||
@api.depends('related_payment_id', 'related_move_id', 'l10n_in_tds_tax_type', 'base', 'tax_id')
|
||||
def _compute_l10n_in_withholding_warning(self):
|
||||
for wizard in self:
|
||||
warnings = {}
|
||||
if wizard.tax_id and wizard.l10n_in_tds_tax_type == 'tds_purchase' and not wizard.related_move_id.commercial_partner_id.l10n_in_pan_entity_id:
|
||||
warnings['lower_tds_tax'] = {
|
||||
'message': _("Please deduct TDS at higher rate if PAN is missing. Ignore if already applied.")
|
||||
}
|
||||
precision = self.currency_id.decimal_places
|
||||
if wizard.related_move_id and float_compare(wizard.related_move_id.amount_untaxed, wizard.base, precision_digits=precision) < 0:
|
||||
message = _("The base amount of TDS is greater than the amount of the %s", wizard.type_name)
|
||||
warnings['lower_move_amount'] = {
|
||||
'message': message
|
||||
}
|
||||
wizard.l10n_in_withholding_warning = warnings
|
||||
|
||||
@api.depends('related_move_id', 'related_payment_id')
|
||||
def _compute_tax_id(self):
|
||||
for wizard in self:
|
||||
sections = wizard.related_move_id._get_l10n_in_tds_tcs_applicable_sections()
|
||||
if sections:
|
||||
accounts_by_section = {}
|
||||
for line in wizard.related_move_id.line_ids:
|
||||
section = line.account_id.l10n_in_tds_tcs_section_id
|
||||
if section in sections:
|
||||
accounts_by_section[section] = line.account_id
|
||||
tax = self.env['account.tax']
|
||||
for section, account in accounts_by_section.items():
|
||||
# Search for the last withhold move line that matches the pan entity and account and section
|
||||
withhold_move_line = self.env['account.move.line'].search([
|
||||
('move_id.l10n_in_withholding_ref_move_id.commercial_partner_id.l10n_in_pan_entity_id', '=', wizard.related_move_id.commercial_partner_id.l10n_in_pan_entity_id.id),
|
||||
('move_id.l10n_in_withholding_ref_move_id.line_ids.account_id', 'in', account.id),
|
||||
('move_id.l10n_in_withholding_ref_move_id.line_ids.l10n_in_tds_tcs_section_id', 'in', section.id),
|
||||
('move_id.state', '=', 'posted'),
|
||||
('tax_ids.l10n_in_section_id', '=', section.id),
|
||||
], limit=1, order='id desc')
|
||||
if withhold_move_line:
|
||||
tax = withhold_move_line.tax_ids.filtered(lambda t: t.l10n_in_section_id == section)
|
||||
break
|
||||
if tax:
|
||||
wizard.tax_id = tax
|
||||
|
||||
@api.depends('tax_id')
|
||||
def _compute_base(self):
|
||||
for wizard in self:
|
||||
if (
|
||||
wizard.tax_id and
|
||||
wizard.related_move_id and
|
||||
wizard.tax_id.l10n_in_section_id not in (self.env.ref('l10n_in.tds_section_194q'), self.env.ref('l10n_in.tds_section_194n'))
|
||||
):
|
||||
sign = -1 if wizard.related_move_id.is_inbound() else 1
|
||||
wizard.base = sign * sum(wizard.related_move_id.line_ids.filtered(lambda l: l.account_id.l10n_in_tds_tcs_section_id == wizard.tax_id.l10n_in_section_id).mapped('balance'))
|
||||
|
||||
@api.depends('tax_id', 'base')
|
||||
def _compute_amount(self):
|
||||
for wizard in self:
|
||||
tax_amount = 0.0
|
||||
if wizard.tax_id:
|
||||
taxes_res = wizard.tax_id.compute_all(
|
||||
wizard.base,
|
||||
currency=wizard.tax_id.company_id.currency_id,
|
||||
quantity=1.0,
|
||||
product=False,
|
||||
partner=False,
|
||||
is_refund=False,
|
||||
)
|
||||
tax_amount = taxes_res['total_included'] - taxes_res['total_excluded']
|
||||
wizard.amount = abs(tax_amount)
|
||||
|
||||
def _get_withhold_type(self):
|
||||
if self.related_move_id:
|
||||
move_type = self.related_move_id.move_type
|
||||
withhold_type = {
|
||||
'out_invoice': 'out_withhold',
|
||||
'in_invoice': 'in_withhold',
|
||||
'out_refund': 'out_refund_withhold',
|
||||
'in_refund': 'in_refund_withhold',
|
||||
}[move_type]
|
||||
else:
|
||||
withhold_type = 'in_withhold' if self.related_payment_id.partner_type == 'supplier' else 'out_withhold'
|
||||
return withhold_type
|
||||
|
||||
# ===== MOVE CREATION METHODS =====
|
||||
def action_create_and_post_withhold(self):
|
||||
self.ensure_one()
|
||||
withholding_account_id = self.company_id.l10n_in_withholding_account_id
|
||||
self._validate_withhold_data_on_post(withholding_account_id)
|
||||
|
||||
# Withhold creation and posting
|
||||
vals = self._prepare_withhold_header()
|
||||
move_lines = self._prepare_withhold_move_lines(withholding_account_id)
|
||||
vals['line_ids'] = [Command.create(line) for line in move_lines]
|
||||
withhold = self.with_company(self.company_id).env['account.move'].create(vals)
|
||||
withhold.action_post()
|
||||
|
||||
# If the withhold is created from a payment, there is no need to reconcile
|
||||
if not self.related_payment_id:
|
||||
wh_reconc = withhold.line_ids.filtered(
|
||||
lambda l: l.account_id.account_type in ('asset_receivable', 'liability_payable'))
|
||||
inv_reconc = self.related_move_id.line_ids.filtered(
|
||||
lambda l: l.account_id.account_type in ('asset_receivable', 'liability_payable') and not l.reconciled)
|
||||
(inv_reconc + wh_reconc).reconcile()
|
||||
related_record = self.related_move_id or self.related_payment_id
|
||||
withhold._message_log(
|
||||
body=Markup("%s %s: <a href='#' data-oe-model='%s' data-oe-id='%s'>%s</a>") % (
|
||||
_("TDS created from"),
|
||||
self.type_name,
|
||||
related_record._name,
|
||||
related_record.id,
|
||||
related_record.name
|
||||
))
|
||||
return withhold
|
||||
|
||||
def _prepare_withhold_header(self):
|
||||
""" Prepare the header for the withhold entry """
|
||||
vals = {
|
||||
'date': self.date,
|
||||
'journal_id': self.journal_id.id,
|
||||
'partner_id': self.related_move_id.partner_id.id or self.related_payment_id.partner_id.id,
|
||||
'move_type': 'entry',
|
||||
'ref': self.reference,
|
||||
'l10n_in_is_withholding': True,
|
||||
'l10n_in_withholding_ref_move_id': self.related_move_id.id or self.related_payment_id.move_id.id,
|
||||
'l10n_in_withholding_ref_payment_id': self.related_payment_id.id
|
||||
}
|
||||
return vals
|
||||
|
||||
def _prepare_withhold_move_lines(self, withholding_account_id):
|
||||
"""
|
||||
Prepare the move lines for the withhold entry
|
||||
"""
|
||||
def append_vals(quantity, price_unit, debit, credit, account_id, tax_ids):
|
||||
return {
|
||||
'quantity': quantity,
|
||||
'price_unit': price_unit,
|
||||
'debit': debit,
|
||||
'credit': credit,
|
||||
'account_id': account_id.id,
|
||||
'tax_ids': tax_ids,
|
||||
}
|
||||
|
||||
vals = []
|
||||
|
||||
partner = self.related_move_id.partner_id or self.related_payment_id.partner_id
|
||||
withhold_type = self._get_withhold_type()
|
||||
|
||||
if withhold_type in ('in_withhold', 'in_refund_withhold'):
|
||||
partner_account = partner.property_account_payable_id
|
||||
else:
|
||||
partner_account = partner.property_account_receivable_id
|
||||
|
||||
# Create move line for withholding tax and the base amount
|
||||
debit = self.base if withhold_type in ('in_withhold', 'out_refund_withhold') else 0.0
|
||||
credit = 0.0 if withhold_type in ('in_withhold', 'out_refund_withhold') else self.base
|
||||
vals.append(append_vals(1.0, self.base, debit, credit, withholding_account_id, [Command.set(self.tax_id.ids)]))
|
||||
total_amount = self.base
|
||||
total_tax = self.amount
|
||||
|
||||
# Create move line for the base amount
|
||||
debit = 0.0 if withhold_type in ('in_withhold', 'out_refund_withhold') else total_amount
|
||||
credit = total_amount if withhold_type in ('in_withhold', 'out_refund_withhold') else 0.0
|
||||
vals.append(append_vals(1.0, total_amount, debit, credit, withholding_account_id, False))
|
||||
|
||||
# Create move line for the tax amount
|
||||
debit = total_tax if withhold_type in ('in_withhold', 'out_refund_withhold') else 0.0
|
||||
credit = 0.0 if withhold_type in ('in_withhold', 'out_refund_withhold') else total_tax
|
||||
vals.append(append_vals(1.0, total_tax, debit, credit, partner_account, False))
|
||||
|
||||
return vals
|
||||
|
||||
def _validate_withhold_data_on_post(self, withholding_account_id):
|
||||
if not withholding_account_id:
|
||||
raise UserError(_("Please configure the withholding account from the settings"))
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="l10n_in_withholding_entry_form_action" model="ir.actions.act_window">
|
||||
<field name="name">Create TDS Entry</field>
|
||||
<field name="res_model">l10n_in.withhold.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<record id="tds_entry_view_form" model="ir.ui.view">
|
||||
<field name="name">l10n_in.withhold.wizard.view.form</field>
|
||||
<field name="model">l10n_in.withhold.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<div class="alert alert-warning mt-1 mb-1" role="alert" invisible="not l10n_in_withholding_warning">
|
||||
<div>
|
||||
<field name="l10n_in_withholding_warning" widget="actionable_errors"/>
|
||||
</div>
|
||||
</div>
|
||||
<sheet>
|
||||
<group>
|
||||
<group id="header_left_group">
|
||||
<field name="date" string="Entry Date"/>
|
||||
<label for="tax_id" string="TDS Section"/>
|
||||
<div>
|
||||
<field name="tax_id" class="oe_inline" domain="[('l10n_in_tax_type', '=', l10n_in_tds_tax_type)]" required="True"
|
||||
options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="tds_deduction" class="oe_inline"
|
||||
decoration-success="tds_deduction == 'lower'"
|
||||
decoration-danger="tds_deduction == 'higher'"
|
||||
decoration-warning="tds_deduction == 'no'"
|
||||
invisible="tds_deduction == 'normal'"
|
||||
/>
|
||||
</div>
|
||||
<field name="base" widget="monetary" options="{'currency_field': 'currency_id'}" required="True"/>
|
||||
<field name="amount" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="currency_id" invisible="1"/> <!-- used to display the currency symbol -->
|
||||
<field name="related_move_id" invisible="1"/> <!-- used to compute the l10n_in_tds_tax_type -->
|
||||
<field name="related_payment_id" invisible="1"/> <!-- used to compute the l10n_in_tds_tax_type -->
|
||||
</group>
|
||||
<group id="header_right_group">
|
||||
<field name="journal_id" domain="[('type', '=', 'general')]"/>
|
||||
<field name="reference"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button string="Apply TDS" type="object" name="action_create_and_post_withhold" class="btn-primary"/>
|
||||
<button string="Discard" special="cancel" class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -1,13 +1,18 @@
|
|||
[project]
|
||||
name = "odoo-bringout-oca-ocb-l10n_in"
|
||||
version = "16.0.0"
|
||||
description = "Indian - Accounting - Odoo addon"
|
||||
description = "Indian - Accounting -
|
||||
Odoo addon
|
||||
"
|
||||
authors = [
|
||||
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
|
||||
]
|
||||
dependencies = [
|
||||
"odoo-bringout-oca-ocb-account_tax_python>=16.0.0",
|
||||
"odoo-bringout-oca-ocb-base_vat>=16.0.0",
|
||||
"odoo-bringout-oca-ocb-account_tax_python>=19.0.0",
|
||||
"odoo-bringout-oca-ocb-base_vat>=19.0.0",
|
||||
"odoo-bringout-oca-ocb-account_debit_note>=19.0.0",
|
||||
"odoo-bringout-oca-ocb-account>=19.0.0",
|
||||
"odoo-bringout-oca-ocb-iap>=19.0.0",
|
||||
"requests>=2.25.1"
|
||||
]
|
||||
readme = "README.md"
|
||||
|
|
@ -17,7 +22,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",
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue