mirror of
https://github.com/bringout/oca-ocb-l10n_asia-pacific.git
synced 2026-04-26 17:42:04 +02:00
Initial commit: L10N_Asia Pacific packages
This commit is contained in:
commit
54c86b612c
828 changed files with 58224 additions and 0 deletions
56
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/README.md
Normal file
56
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/README.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Indian - E-waybill
|
||||
|
||||
|
||||
Indian - E-waybill
|
||||
====================================
|
||||
To submit E-waybill through API to the government.
|
||||
We use "Tera Software Limited" as GSP
|
||||
|
||||
Step 1: First you need to create an API username and password in the E-waybill portal.
|
||||
Step 2: Switch to company related to that GST number
|
||||
Step 3: Set that username and password in Odoo (Goto: Invoicing/Accounting -> Configration -> Settings -> Indian Electronic WayBill or find "E-waybill" in search bar)
|
||||
Step 4: Repeat steps 1,2,3 for all GSTIN you have in odoo. If you have a multi-company with the same GST number then perform step 1 for the first company only.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install odoo-bringout-oca-ocb-l10n_in_edi_ewaybill
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
This addon depends on:
|
||||
- l10n_in_edi
|
||||
|
||||
## Manifest Information
|
||||
|
||||
- **Name**: Indian - E-waybill
|
||||
- **Version**: 1.03.00
|
||||
- **Category**: Accounting/Localizations/EDI
|
||||
- **License**: LGPL-3
|
||||
- **Installable**: True
|
||||
|
||||
## Source
|
||||
|
||||
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `l10n_in_edi_ewaybill`.
|
||||
|
||||
## 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
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
U[Users] -->|HTTP| V[Views and QWeb Templates]
|
||||
V --> C[Controllers]
|
||||
V --> W[Wizards – Transient Models]
|
||||
C --> M[Models and ORM]
|
||||
W --> M
|
||||
M --> R[Reports]
|
||||
DX[Data XML] --> M
|
||||
S[Security – ACLs and Groups] -. enforces .-> M
|
||||
|
||||
subgraph L10n_in_edi_ewaybill Module - l10n_in_edi_ewaybill
|
||||
direction LR
|
||||
M:::layer
|
||||
W:::layer
|
||||
C:::layer
|
||||
V:::layer
|
||||
R:::layer
|
||||
S:::layer
|
||||
DX:::layer
|
||||
end
|
||||
|
||||
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
|
||||
```
|
||||
|
||||
Notes
|
||||
- Views include tree/form/kanban templates and report templates.
|
||||
- Controllers provide website/portal routes when present.
|
||||
- Wizards are UI flows implemented with `models.TransientModel`.
|
||||
- Data XML loads data/demo records; Security defines groups and access.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Configuration
|
||||
|
||||
Refer to Odoo settings for l10n_in_edi_ewaybill. Configure related models, access rights, and options as needed.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Controllers
|
||||
|
||||
This module does not define custom HTTP controllers.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Dependencies
|
||||
|
||||
This addon depends on:
|
||||
|
||||
- [l10n_in_edi](../../odoo-bringout-oca-ocb-l10n_in_edi)
|
||||
4
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/doc/FAQ.md
Normal file
4
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/doc/FAQ.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# FAQ
|
||||
|
||||
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
|
||||
- Q: How to enable? A: Start server with --addon l10n_in_edi_ewaybill or install in UI.
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Install
|
||||
|
||||
```bash
|
||||
pip install odoo-bringout-oca-ocb-l10n_in_edi_ewaybill"
|
||||
# or
|
||||
uv pip install odoo-bringout-oca-ocb-l10n_in_edi_ewaybill"
|
||||
```
|
||||
16
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/doc/MODELS.md
Normal file
16
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/doc/MODELS.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Models
|
||||
|
||||
Detected core models and extensions in l10n_in_edi_ewaybill.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class l10n_in_ewaybill_type
|
||||
class account_edi_format
|
||||
class account_move
|
||||
class res_company
|
||||
class res_config_settings
|
||||
```
|
||||
|
||||
Notes
|
||||
- Classes show model technical names; fields omitted for brevity.
|
||||
- Items listed under _inherit are extensions of existing models.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# Overview
|
||||
|
||||
Packaged Odoo addon: l10n_in_edi_ewaybill. Provides features documented in upstream Odoo 16 under this addon.
|
||||
|
||||
- Source: OCA/OCB 16.0, addon l10n_in_edi_ewaybill
|
||||
- License: LGPL-3
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Reports
|
||||
|
||||
This module does not define custom reports.
|
||||
34
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/doc/SECURITY.md
Normal file
34
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/doc/SECURITY.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Security
|
||||
|
||||
Access control and security definitions in l10n_in_edi_ewaybill.
|
||||
|
||||
## Access Control Lists (ACLs)
|
||||
|
||||
Model access permissions defined in:
|
||||
- **[ir.model.access.csv](../l10n_in_edi_ewaybill/security/ir.model.access.csv)**
|
||||
- 1 model access rules
|
||||
|
||||
## Record Rules
|
||||
|
||||
Row-level security rules defined in:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Security Layers"
|
||||
A[Users] --> B[Groups]
|
||||
B --> C[Access Control Lists]
|
||||
C --> D[Models]
|
||||
B --> E[Record Rules]
|
||||
E --> F[Individual Records]
|
||||
end
|
||||
```
|
||||
|
||||
Security files overview:
|
||||
- **[ir.model.access.csv](../l10n_in_edi_ewaybill/security/ir.model.access.csv)**
|
||||
- Model access permissions (CRUD rights)
|
||||
|
||||
Notes
|
||||
- Access Control Lists define which groups can access which models
|
||||
- Record Rules provide row-level security (filter records by user/group)
|
||||
- Security groups organize users and define permission sets
|
||||
- All security is enforced at the ORM level by Odoo
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Troubleshooting
|
||||
|
||||
- Ensure Python and Odoo environment matches repo guidance.
|
||||
- Check database connectivity and logs if startup fails.
|
||||
- Validate that dependent addons listed in DEPENDENCIES.md are installed.
|
||||
7
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/doc/USAGE.md
Normal file
7
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/doc/USAGE.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Usage
|
||||
|
||||
Start Odoo including this addon (from repo root):
|
||||
|
||||
```bash
|
||||
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon l10n_in_edi_ewaybill
|
||||
```
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Wizards
|
||||
|
||||
This module does not include UI wizards.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
from . import demo
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
{
|
||||
"name": """Indian - E-waybill""",
|
||||
"version": "1.03.00",
|
||||
"icon": "/l10n_in/static/description/icon.png",
|
||||
"category": "Accounting/Localizations/EDI",
|
||||
"depends": [
|
||||
"l10n_in_edi",
|
||||
],
|
||||
"description": """
|
||||
Indian - E-waybill
|
||||
====================================
|
||||
To submit E-waybill through API to the government.
|
||||
We use "Tera Software Limited" as GSP
|
||||
|
||||
Step 1: First you need to create an API username and password in the E-waybill portal.
|
||||
Step 2: Switch to company related to that GST number
|
||||
Step 3: Set that username and password in Odoo (Goto: Invoicing/Accounting -> Configration -> Settings -> Indian Electronic WayBill or find "E-waybill" in search bar)
|
||||
Step 4: Repeat steps 1,2,3 for all GSTIN you have in odoo. If you have a multi-company with the same GST number then perform step 1 for the first company only.
|
||||
""",
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"data/account_edi_data.xml",
|
||||
"data/ewaybill_type_data.xml",
|
||||
"views/account_move_views.xml",
|
||||
"views/edi_pdf_report.xml",
|
||||
"views/res_config_settings_views.xml",
|
||||
],
|
||||
"demo": [
|
||||
"demo/demo_company.xml",
|
||||
"demo/res_partner_demo.xml",
|
||||
"demo/account_invoice_demo.xml",
|
||||
],
|
||||
"installable": True,
|
||||
# not applicable when company is related to service industry so auto install is False
|
||||
"auto_install": False,
|
||||
"license": "LGPL-3",
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="edi_in_ewaybill_json_1_03" model="account.edi.format">
|
||||
<field name="name">E-waybill (IN)</field>
|
||||
<field name="code">in_ewaybill_1_03</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Document Type -->
|
||||
<record id="type_tax_invoice_sub_type_supply" model="l10n.in.ewaybill.type">
|
||||
<field name="name">Tax Invoice</field>
|
||||
<field name="code">INV</field>
|
||||
<field name="sub_type">Supply</field>
|
||||
<field name="sub_type_code">1</field>
|
||||
<field name="allowed_supply_type">both</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="type_tax_invoice_sub_type_export" model="l10n.in.ewaybill.type">
|
||||
<field name="name">Tax Invoice</field>
|
||||
<field name="code">INV</field>
|
||||
<field name="sub_type">Export</field>
|
||||
<field name="sub_type_code">3</field>
|
||||
<field name="allowed_supply_type">out</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="type_tax_invoice_sub_type_skd_ckd_lots" model="l10n.in.ewaybill.type">
|
||||
<field name="name">Tax Invoice</field>
|
||||
<field name="code">INV</field>
|
||||
<field name="sub_type">SKD/CKD/Lots</field>
|
||||
<field name="sub_type_code">9</field>
|
||||
<field name="allowed_supply_type">both</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="type_bill_of_supply_sub_type_supply" model="l10n.in.ewaybill.type">
|
||||
<field name="name">Bill of Supply</field>
|
||||
<field name="code">BIL</field>
|
||||
<field name="sub_type">Supply</field>
|
||||
<field name="sub_type_code">1</field>
|
||||
<field name="allowed_supply_type">both</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="type_bill_of_supply_sub_type_export" model="l10n.in.ewaybill.type">
|
||||
<field name="name">Bill of Supply</field>
|
||||
<field name="code">BIL</field>
|
||||
<field name="sub_type">Export</field>
|
||||
<field name="sub_type_code">3</field>
|
||||
<field name="allowed_supply_type">out</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="type_bill_of_supply_sub_type_skd_ckd_lots" model="l10n.in.ewaybill.type">
|
||||
<field name="name">Bill of Supply</field>
|
||||
<field name="code">BIL</field>
|
||||
<field name="sub_type">SKD/CKD/Lots</field>
|
||||
<field name="sub_type_code">9</field>
|
||||
<field name="allowed_supply_type">both</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="type_bill_of_entry_sub_type_import" model="l10n.in.ewaybill.type">
|
||||
<field name="name">Bill of Entry</field>
|
||||
<field name="code">BOE</field>
|
||||
<field name="sub_type">Import</field>
|
||||
<field name="sub_type_code">2</field>
|
||||
<field name="allowed_supply_type">in</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
<record id="type_bill_of_entry_sub_skd_ckd_lots" model="l10n.in.ewaybill.type">
|
||||
<field name="name">Bill of Entry</field>
|
||||
<field name="code">BOE</field>
|
||||
<field name="sub_type">SKD/CKD/Lots</field>
|
||||
<field name="sub_type_code">9</field>
|
||||
<field name="allowed_supply_type">in</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
-- disable l10n_in_edi_ewaybill integration
|
||||
UPDATE res_company
|
||||
SET l10n_in_edi_ewaybill_username = NULL,
|
||||
l10n_in_edi_ewaybill_password = NULL,
|
||||
l10n_in_edi_ewaybill_auth_validity = NULL;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import chart_template
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<function model="account.journal" name="write">
|
||||
<value model="account.journal"
|
||||
eval="obj().search([
|
||||
('type', '=', 'sale'),
|
||||
('company_id', '=', ref('l10n_in_edi_ewaybill.demo_company_in_ewaybill'))], limit=1).ids"/>
|
||||
<value eval="{'edi_format_ids': [(6,0,[ref('edi_in_ewaybill_json_1_03')])]}"/>
|
||||
</function>
|
||||
<!-- Demo of B2B (business-to-business) Taxable supplies made to other registered person.-->
|
||||
<record id="demo_invoice_b2b_ewaybill" model="account.move">
|
||||
<field name="move_type">out_invoice</field>
|
||||
<field name="partner_id" ref="res_partner_registered_customer_ewaybill"/>
|
||||
<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="l10n_in_type_id" ref="type_tax_invoice_sub_type_supply"/>
|
||||
<field name="l10n_in_distance">20</field>
|
||||
<field name="l10n_in_mode">1</field>
|
||||
<field name="l10n_in_vehicle_no">GJ11OD1234</field>
|
||||
<field name="l10n_in_vehicle_type">R</field>
|
||||
<field name="journal_id" model="account.journal"
|
||||
eval="obj().search([
|
||||
('type', '=', 'sale'),
|
||||
('company_id', '=', ref('l10n_in_edi_ewaybill.demo_company_in_ewaybill'))], 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_edi_ewaybill.demo_company_in_ewaybill')),
|
||||
('type_tax_use', '=', 'sale'),
|
||||
('amount','=', 28),
|
||||
('tax_group_id', '=', ref('l10n_in.igst_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_edi_ewaybill.demo_company_in_ewaybill')),
|
||||
('type_tax_use', '=', 'sale'),
|
||||
('amount','=', 18),
|
||||
('tax_group_id', '=', ref('l10n_in.igst_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_edi_ewaybill.demo_company_in_ewaybill')),
|
||||
('type_tax_use', '=', 'sale'),
|
||||
'|',
|
||||
'&',
|
||||
('amount', '=', 18),
|
||||
('tax_group_id', '=', ref('l10n_in.igst_group')),
|
||||
'&',
|
||||
('tax_group_id', '=', ref('l10n_in.cess_group')),
|
||||
('children_tax_ids.amount','=', 5)
|
||||
], limit=2).ids)]
|
||||
}),
|
||||
]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, api, Command
|
||||
|
||||
|
||||
class AccountChartTemplate(models.Model):
|
||||
_inherit = 'account.chart.template'
|
||||
|
||||
@api.model
|
||||
def _get_demo_data(self):
|
||||
"""We need to deactivate einvoice here, as we can not send e-invoice and e-waybill in the same demo company"""
|
||||
if self.env.company == self.env.ref('l10n_in_edi_ewaybill.demo_company_in_ewaybill'):
|
||||
val = self.env['account.journal'].search([
|
||||
('type', '=', 'sale'),
|
||||
('company_id', '=', self.env.ref('l10n_in_edi_ewaybill.demo_company_in_ewaybill').id)])
|
||||
val.write({'edi_format_ids': [Command.unlink(self.env.ref('l10n_in_edi.edi_in_einvoice_json_1_03').id)]})
|
||||
return super()._get_demo_data()
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="partner_demo_company_in_ewaybill" model="res.partner">
|
||||
<field name="name">E-Waybill Company</field>
|
||||
<field name="vat">05AAACH6188F1ZM</field>
|
||||
<field name="street">46B 49B, RAIPUR</field>
|
||||
<field name="street2">BHAGWANPUR, ROORKEE</field>
|
||||
<field name="city">Haridwar</field>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
<field name="state_id" ref="base.state_in_uk"/>
|
||||
<field name="zip">247667</field>
|
||||
</record>
|
||||
|
||||
<record id="demo_company_in_ewaybill" model="res.company">
|
||||
<field name="name">E-Waybill Company</field>
|
||||
<field name="partner_id" ref="partner_demo_company_in_ewaybill"/>
|
||||
<field name="l10n_in_edi_ewaybill_username">05AAACH6188F1ZM</field>
|
||||
<field name="l10n_in_edi_ewaybill_password">abc123@@</field>
|
||||
</record>
|
||||
|
||||
<function model="res.company" name="_onchange_country_id">
|
||||
<value eval="[ref('demo_company_in_ewaybill')]"/>
|
||||
</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('demo_company_in_ewaybill'))]}"/>
|
||||
</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_edi_ewaybill.demo_company_in_ewaybill')"/>
|
||||
</function>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="res_partner_registered_customer_ewaybill" model="res.partner">
|
||||
<field name="name">Registered Customer (E-Waybill)</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">19, Ground Floor</field>
|
||||
<field name="street2">Survey Road</field>
|
||||
<field name="city">Dehradun</field>
|
||||
<field name="zip">248001</field>
|
||||
<field name="state_id" ref="base.state_in_uk"/>
|
||||
<field name="country_id" ref="base.in"/>
|
||||
<field name="vat">05AAACH6605F1Z0</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import ewaybill_type
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import account_move
|
||||
from . import account_edi_format
|
||||
|
|
@ -0,0 +1,651 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import re
|
||||
import json
|
||||
from datetime import timedelta
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.tools import html_escape
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.addons.iap import jsonrpc
|
||||
from odoo.addons.l10n_in_edi.models.account_edi_format import DEFAULT_IAP_ENDPOINT, DEFAULT_IAP_TEST_ENDPOINT
|
||||
|
||||
from .error_codes import ERROR_CODES
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccountEdiFormat(models.Model):
|
||||
_inherit = "account.edi.format"
|
||||
|
||||
def _l10n_in_edi_ewaybill_base_irn_or_direct(self, move):
|
||||
"""
|
||||
There is two type of api call to create E-waybill
|
||||
1. base on IRN, IRN is number created when we do E-invoice
|
||||
2. direct call, when E-invoice not aplicable or it"s credit not
|
||||
"""
|
||||
if move.move_type == "out_refund":
|
||||
return "direct"
|
||||
einvoice_in_edi_format = move.journal_id.edi_format_ids.filtered(lambda f: f.code == "in_einvoice_1_03")
|
||||
return einvoice_in_edi_format and einvoice_in_edi_format._get_move_applicability(move) and "irn" or "direct"
|
||||
|
||||
def _is_compatible_with_journal(self, journal):
|
||||
if self.code == "in_ewaybill_1_03":
|
||||
return journal.type in ("sale", "purchase")
|
||||
return super()._is_compatible_with_journal(journal)
|
||||
|
||||
def _is_enabled_by_default_on_journal(self, journal):
|
||||
"""
|
||||
It's sent with a button action on the invoice so it's disabled by default
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.code == "in_ewaybill_1_03":
|
||||
return False
|
||||
return super()._is_enabled_by_default_on_journal(journal)
|
||||
|
||||
def _get_move_applicability(self, invoice):
|
||||
self.ensure_one()
|
||||
if self.code != 'in_ewaybill_1_03':
|
||||
return super()._get_move_applicability(invoice)
|
||||
|
||||
if invoice.is_invoice() and invoice.country_code == 'IN':
|
||||
res = {
|
||||
'post': self._l10n_in_edi_ewaybill_post_invoice_edi,
|
||||
'cancel': self._l10n_in_edi_ewaybill_cancel_invoice,
|
||||
'edi_content': self._l10n_in_edi_ewaybill_json_invoice_content,
|
||||
}
|
||||
base = self._l10n_in_edi_ewaybill_base_irn_or_direct(invoice)
|
||||
if base == 'irn':
|
||||
res.update({
|
||||
'post': self._l10n_in_edi_ewaybill_irn_post_invoice_edi,
|
||||
'edi_content': self._l10n_in_edi_ewaybill_irn_json_invoice_content,
|
||||
})
|
||||
return res
|
||||
|
||||
def _needs_web_services(self):
|
||||
self.ensure_one()
|
||||
return self.code == "in_ewaybill_1_03" or super()._needs_web_services()
|
||||
|
||||
def _l10n_in_edi_ewaybill_irn_json_invoice_content(self, move):
|
||||
return json.dumps(self._l10n_in_edi_irn_ewaybill_generate_json(move)).encode()
|
||||
|
||||
def _l10n_in_edi_ewaybill_json_invoice_content(self, move):
|
||||
return json.dumps(self._l10n_in_edi_ewaybill_generate_json(move)).encode()
|
||||
|
||||
def _check_move_configuration(self, move):
|
||||
if self.code != "in_ewaybill_1_03":
|
||||
return super()._check_move_configuration(move)
|
||||
error_message = []
|
||||
base = self._l10n_in_edi_ewaybill_base_irn_or_direct(move)
|
||||
if not move.l10n_in_type_id and base == "direct":
|
||||
error_message.append(_("- Document Type"))
|
||||
if not move.l10n_in_mode:
|
||||
error_message.append(_("- Transportation Mode"))
|
||||
elif move.l10n_in_mode == "0" and not move.l10n_in_transporter_id:
|
||||
error_message.append(_("- Transporter is required when E-waybill is managed by transporter"))
|
||||
elif move.l10n_in_mode == "0" and move.l10n_in_transporter_id and not move.l10n_in_transporter_id.vat:
|
||||
error_message.append(_("- Selected Transporter is missing GSTIN"))
|
||||
elif move.l10n_in_mode == "1":
|
||||
if not move.l10n_in_vehicle_no and move.l10n_in_vehicle_type:
|
||||
error_message.append(_("- Vehicle Number and Type is required when Transportation Mode is By Road"))
|
||||
elif move.l10n_in_mode in ("2", "3", "4"):
|
||||
if not move.l10n_in_transportation_doc_no and move.l10n_in_transportation_doc_date:
|
||||
error_message.append(_("- Transport document number and date is required when Transportation Mode is Rail,Air or Ship"))
|
||||
if error_message:
|
||||
error_message.insert(0, _("The following information are missing on the invoice (see eWayBill tab):"))
|
||||
goods_lines = move.invoice_line_ids.filtered(lambda line: not (line.display_type in ('line_section', 'line_note', 'rounding') or line.product_id.type == "service"))
|
||||
if not goods_lines:
|
||||
error_message.append(_('You need at least one product having "Product Type" as stockable or consumable.'))
|
||||
if base == "irn":
|
||||
# already checked by E-invoice (l10n_in_edi) so no need to check
|
||||
return error_message
|
||||
is_purchase = move.is_purchase_document(include_receipts=True)
|
||||
error_message += self._l10n_in_validate_partner(move.partner_id)
|
||||
error_message += self._l10n_in_validate_partner(move.company_id.partner_id, is_company=True)
|
||||
if not re.match("^.{1,16}$", is_purchase and move.ref or move.name):
|
||||
error_message.append(_("%s number should be set and not more than 16 characters",
|
||||
(is_purchase and "Bill Reference" or "Invoice")))
|
||||
for line in goods_lines:
|
||||
if line.product_id:
|
||||
hsn_code = self._l10n_in_edi_extract_digits(line.product_id.l10n_in_hsn_code)
|
||||
if not hsn_code:
|
||||
error_message.append(_("HSN code is not set in product %s", line.product_id.name))
|
||||
elif not re.match("^[0-9]+$", hsn_code):
|
||||
error_message.append(_(
|
||||
"Invalid HSN Code (%s) in product %s", hsn_code, line.product_id.name
|
||||
))
|
||||
else:
|
||||
error_message.append(_("product is required to get HSN code"))
|
||||
if error_message:
|
||||
error_message.insert(0, _("Impossible to send the Ewaybill."))
|
||||
return error_message
|
||||
|
||||
def _l10n_in_edi_ewaybill_cancel_invoice(self, invoices):
|
||||
if self.code != "in_ewaybill_1_03":
|
||||
return super()._cancel_invoice_edi(invoices)
|
||||
response = {}
|
||||
res = {}
|
||||
ewaybill_response_json = invoices._get_l10n_in_edi_ewaybill_response_json()
|
||||
cancel_json = {
|
||||
"ewbNo": ewaybill_response_json.get("ewayBillNo") or ewaybill_response_json.get("EwbNo"),
|
||||
"cancelRsnCode": int(invoices.l10n_in_edi_cancel_reason),
|
||||
"cancelRmrk": invoices.l10n_in_edi_cancel_remarks,
|
||||
}
|
||||
response = self._l10n_in_edi_ewaybill_cancel(invoices.company_id, cancel_json)
|
||||
if response.get("error"):
|
||||
error = response["error"]
|
||||
error_codes = [e.get("code") for e in error]
|
||||
if "238" in error_codes:
|
||||
# Invalid token eror then create new token and send generate request again.
|
||||
# This happen when authenticate called from another odoo instance with same credentials (like. Demo/Test)
|
||||
authenticate_response = self._l10n_in_edi_ewaybill_authenticate(invoices.company_id)
|
||||
if not authenticate_response.get("error"):
|
||||
error = []
|
||||
response = self._l10n_in_edi_ewaybill_cancel(invoices.company_id, cancel_json)
|
||||
if response.get("error"):
|
||||
error = response["error"]
|
||||
error_codes = [e.get("code") for e in error]
|
||||
if "312" in error_codes:
|
||||
# E-waybill is already canceled
|
||||
# this happens when timeout from the Government portal but IRN is generated
|
||||
error_message = "<br/>".join(["[%s] %s" % (e.get("code"), html_escape(e.get("message"))) for e in error])
|
||||
error = []
|
||||
response = {"data": ""}
|
||||
odoobot = self.env.ref("base.partner_root")
|
||||
invoices.message_post(author_id=odoobot.id, body=
|
||||
"%s<br/>%s:<br/>%s" %(
|
||||
_("Somehow this E-waybill has been canceled in the government portal before. You can verify by checking the details into the government (https://ewaybillgst.gov.in/Others/EBPrintnew.aspx)"),
|
||||
_("Error"),
|
||||
error_message
|
||||
)
|
||||
)
|
||||
if "no-credit" in error_codes:
|
||||
res[invoices] = {
|
||||
"success": False,
|
||||
"error": self._l10n_in_edi_get_iap_buy_credits_message(invoices.company_id),
|
||||
"blocking_level": "error",
|
||||
}
|
||||
elif error:
|
||||
error_message = "<br/>".join(["[%s] %s" % (e.get("code"), html_escape(e.get("message"))) for e in error])
|
||||
blocking_level = "error"
|
||||
if "404" in error_codes:
|
||||
blocking_level = "warning"
|
||||
res[invoices] = {
|
||||
"success": False,
|
||||
"error": error_message,
|
||||
"blocking_level": blocking_level,
|
||||
}
|
||||
if not response.get("error"):
|
||||
json_dump = json.dumps(response.get("data"))
|
||||
json_name = "%s_ewaybill_cancel.json" % (invoices.name.replace("/", "_"))
|
||||
attachment = self.env["ir.attachment"].create({
|
||||
"name": json_name,
|
||||
"raw": json_dump.encode(),
|
||||
"res_model": "account.move",
|
||||
"res_id": invoices.id,
|
||||
"mimetype": "application/json",
|
||||
})
|
||||
inv_res = {"success": True, "attachment": attachment}
|
||||
res[invoices] = inv_res
|
||||
return res
|
||||
|
||||
def _l10n_in_edi_ewaybill_irn_post_invoice_edi(self, invoices):
|
||||
response = {}
|
||||
res = {}
|
||||
generate_json = self._l10n_in_edi_irn_ewaybill_generate_json(invoices)
|
||||
response = self._l10n_in_edi_irn_ewaybill_generate(invoices.company_id, generate_json)
|
||||
if response.get("error"):
|
||||
error = response["error"]
|
||||
error_codes = [e.get("code") for e in error]
|
||||
if "1005" in error_codes:
|
||||
# Invalid token eror then create new token and send generate request again.
|
||||
# This happen when authenticate called from another odoo instance with same credentials (like. Demo/Test)
|
||||
authenticate_response = self._l10n_in_edi_authenticate(invoices.company_id)
|
||||
if not authenticate_response.get("error"):
|
||||
error = []
|
||||
response = self._l10n_in_edi_irn_ewaybill_generate(invoices.company_id, generate_json)
|
||||
if response.get("error"):
|
||||
error = response["error"]
|
||||
error_codes = [e.get("code") for e in error]
|
||||
if "4002" in error_codes or "4026" in error_codes:
|
||||
# Get E-waybill by details in case of IRN is already generated
|
||||
# this happens when timeout from the Government portal but E-waybill is generated
|
||||
response = self._l10n_in_edi_irn_ewaybill_get(invoices.company_id, generate_json.get("Irn"))
|
||||
if not response.get("error"):
|
||||
error = []
|
||||
odoobot = self.env.ref("base.partner_root")
|
||||
invoices.message_post(author_id=odoobot.id, body=
|
||||
_("Somehow this E-waybill has been generated in the government portal before. You can verify by checking the invoice details into the government (https://ewaybillgst.gov.in/Others/EBPrintnew.aspx)")
|
||||
)
|
||||
|
||||
if "no-credit" in error_codes:
|
||||
res[invoices] = {
|
||||
"success": False,
|
||||
"error": self._l10n_in_edi_get_iap_buy_credits_message(invoices.company_id),
|
||||
"blocking_level": "error",
|
||||
}
|
||||
elif error:
|
||||
error_message = "<br/>".join(["[%s] %s" % (e.get("code"), html_escape(e.get("message"))) for e in error])
|
||||
blocking_level = "error"
|
||||
if "404" in error_codes or "waiting" in error_codes:
|
||||
blocking_level = "warning"
|
||||
res[invoices] = {
|
||||
"success": False,
|
||||
"error": error_message,
|
||||
"blocking_level": blocking_level,
|
||||
}
|
||||
if not response.get("error"):
|
||||
json_dump = json.dumps(response.get("data"))
|
||||
json_name = "%s_irn_ewaybill.json" % (invoices.name.replace("/", "_"))
|
||||
attachment = self.env["ir.attachment"].create({
|
||||
"name": json_name,
|
||||
"raw": json_dump.encode(),
|
||||
"res_model": "account.move",
|
||||
"res_id": invoices.id,
|
||||
"mimetype": "application/json",
|
||||
})
|
||||
inv_res = {"success": True, "attachment": attachment}
|
||||
res[invoices] = inv_res
|
||||
body = Markup("""
|
||||
<b>{}</b><br/>
|
||||
<ul>
|
||||
<li>{}<i class="o-mail-Message-trackingSeparator fa fa-long-arrow-right mx-1 text-600"></i>{}</li>
|
||||
<li>{}<i class="o-mail-Message-trackingSeparator fa fa-long-arrow-right mx-1 text-600"></i>{}</li>
|
||||
</ul>
|
||||
""").format(
|
||||
_('E-wayBill Sent'),
|
||||
_('Number'),
|
||||
str(response.get("data", {}).get('EwbNo')),
|
||||
_('Validity'),
|
||||
str(response.get("data", {}).get('EwbValidTill'))
|
||||
)
|
||||
|
||||
invoices.message_post(body=body)
|
||||
return res
|
||||
|
||||
def _l10n_in_edi_irn_ewaybill_generate_json(self, invoice):
|
||||
json_payload = {
|
||||
"Irn": invoice._get_l10n_in_edi_response_json().get("Irn"),
|
||||
"Distance": invoice.l10n_in_distance,
|
||||
}
|
||||
if invoice.l10n_in_mode == "0":
|
||||
json_payload.update({
|
||||
"TransId": invoice.l10n_in_transporter_id.vat,
|
||||
"TransName": invoice.l10n_in_transporter_id.name,
|
||||
})
|
||||
elif invoice.l10n_in_mode == "1":
|
||||
json_payload.update({
|
||||
"TransMode": invoice.l10n_in_mode,
|
||||
"VehNo": invoice.l10n_in_vehicle_no,
|
||||
"VehType": invoice.l10n_in_vehicle_type,
|
||||
})
|
||||
elif invoice.l10n_in_mode in ("2", "3", "4"):
|
||||
doc_date = invoice.l10n_in_transportation_doc_date
|
||||
json_payload.update({
|
||||
"TransMode": invoice.l10n_in_mode,
|
||||
"TransDocDt": doc_date and doc_date.strftime("%d/%m/%Y") or False,
|
||||
"TransDocNo": invoice.l10n_in_transportation_doc_no,
|
||||
})
|
||||
return json_payload
|
||||
|
||||
def _l10n_in_edi_ewaybill_post_invoice_edi(self, invoices):
|
||||
response = {}
|
||||
res = {}
|
||||
generate_json = self._l10n_in_edi_ewaybill_generate_json(invoices)
|
||||
response = self._l10n_in_edi_ewaybill_generate(invoices.company_id, generate_json)
|
||||
if response.get("error"):
|
||||
error = response["error"]
|
||||
error_codes = [e.get("code") for e in error]
|
||||
if "238" in error_codes:
|
||||
# Invalid token eror then create new token and send generate request again.
|
||||
# This happen when authenticate called from another odoo instance with same credentials (like. Demo/Test)
|
||||
authenticate_response = self._l10n_in_edi_ewaybill_authenticate(invoices.company_id)
|
||||
if not authenticate_response.get("error"):
|
||||
error = []
|
||||
response = self._l10n_in_edi_ewaybill_generate(invoices.company_id, generate_json)
|
||||
if response.get("error"):
|
||||
error = response["error"]
|
||||
error_codes = [e.get("code") for e in error]
|
||||
if "604" in error_codes:
|
||||
# Get E-waybill by details in case of E-waybill is already generated
|
||||
# this happens when timeout from the Government portal but E-waybill is generated
|
||||
response = self._l10n_in_edi_ewaybill_get_by_consigner(
|
||||
invoices.company_id, generate_json.get("docType"), generate_json.get("docNo"))
|
||||
if not response.get("error"):
|
||||
error = []
|
||||
odoobot = self.env.ref("base.partner_root")
|
||||
invoices.message_post(author_id=odoobot.id, body=
|
||||
_("Somehow this E-waybill has been generated in the government portal before. You can verify by checking the invoice details into the government (https://ewaybillgst.gov.in/Others/EBPrintnew.aspx)")
|
||||
)
|
||||
if "no-credit" in error_codes:
|
||||
res[invoices] = {
|
||||
"success": False,
|
||||
"error": self._l10n_in_edi_get_iap_buy_credits_message(invoices.company_id),
|
||||
"blocking_level": "error",
|
||||
}
|
||||
elif error:
|
||||
error_message = "<br/>".join(["[%s] %s" % (e.get("code"), html_escape(e.get("message"))) for e in error])
|
||||
blocking_level = "error"
|
||||
if "404" in error_codes:
|
||||
blocking_level = "warning"
|
||||
res[invoices] = {
|
||||
"success": False,
|
||||
"error": error_message,
|
||||
"blocking_level": blocking_level,
|
||||
}
|
||||
if not response.get("error"):
|
||||
json_dump = json.dumps(response.get("data"))
|
||||
json_name = "%s_ewaybill.json" % (invoices.name.replace("/", "_"))
|
||||
attachment = self.env["ir.attachment"].create({
|
||||
"name": json_name,
|
||||
"raw": json_dump.encode(),
|
||||
"res_model": "account.move",
|
||||
"res_id": invoices.id,
|
||||
"mimetype": "application/json",
|
||||
})
|
||||
inv_res = {"success": True, "attachment": attachment}
|
||||
res[invoices] = inv_res
|
||||
body = Markup("""
|
||||
<b>{}</b><br/>
|
||||
<ul>
|
||||
<li>{}<i class="o-mail-Message-trackingSeparator fa fa-long-arrow-right mx-1 text-600"></i>{}</li>
|
||||
<li>{}<i class="o-mail-Message-trackingSeparator fa fa-long-arrow-right mx-1 text-600"></i>{}</li>
|
||||
</ul>
|
||||
""").format(
|
||||
_('E-wayBill Sent'),
|
||||
_('Number'),
|
||||
str(response.get("data", {}).get('ewayBillNo')),
|
||||
_('Validity'),
|
||||
str(response.get("data", {}).get('validUpto'))
|
||||
)
|
||||
invoices.message_post(body=body)
|
||||
return res
|
||||
|
||||
def _l10n_in_edi_ewaybill_get_error_message(self, code):
|
||||
error_message = ERROR_CODES.get(code)
|
||||
return error_message or _("We don't know the error message for this error code. Please contact support.")
|
||||
|
||||
def _get_l10n_in_edi_saler_buyer_party(self, move):
|
||||
res = super()._get_l10n_in_edi_saler_buyer_party(move)
|
||||
if move.is_outbound() and self.code == 'in_ewaybill_1_03':
|
||||
res = {
|
||||
"seller_details": move.partner_id,
|
||||
"dispatch_details": move.partner_shipping_id or move.partner_id,
|
||||
"buyer_details": move.company_id.partner_id,
|
||||
"ship_to_details": move._l10n_in_get_warehouse_address() or move.company_id.partner_id,
|
||||
}
|
||||
return res
|
||||
|
||||
def _l10n_in_edi_ewaybill_generate_json(self, invoices):
|
||||
def get_transaction_type(seller_details, dispatch_details, buyer_details, ship_to_details):
|
||||
"""
|
||||
1 - Regular
|
||||
2 - Bill To - Ship To
|
||||
3 - Bill From - Dispatch From
|
||||
4 - Combination of 2 and 3
|
||||
"""
|
||||
if seller_details != dispatch_details and buyer_details != ship_to_details:
|
||||
return 4
|
||||
elif seller_details != dispatch_details:
|
||||
return 3
|
||||
elif buyer_details != ship_to_details:
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
|
||||
saler_buyer = self._get_l10n_in_edi_saler_buyer_party(invoices)
|
||||
seller_details = saler_buyer.get("seller_details")
|
||||
dispatch_details = saler_buyer.get("dispatch_details")
|
||||
buyer_details = saler_buyer.get("buyer_details")
|
||||
ship_to_details = saler_buyer.get("ship_to_details")
|
||||
sign = invoices.is_inbound() and -1 or 1
|
||||
extract_digits = self._l10n_in_edi_extract_digits
|
||||
tax_details = self._l10n_in_prepare_edi_tax_details(invoices)
|
||||
tax_details_by_code = self._get_l10n_in_tax_details_by_line_code(tax_details.get("tax_details", {}))
|
||||
invoice_line_tax_details = tax_details.get("tax_details_per_record")
|
||||
rounding_amount = sum(line.balance for line in invoices.line_ids if line.display_type == 'rounding') * sign
|
||||
json_payload = {
|
||||
# Note:
|
||||
# Customer Invoice, Sales Receipt and Vendor Credit Note are Outgoing
|
||||
# Vendor Bill, Purchase Receipt, and Customer Credit Note are Incoming
|
||||
"supplyType": invoices.is_outbound() and "I" or "O",
|
||||
"subSupplyType": invoices.l10n_in_type_id.sub_type_code,
|
||||
"docType": invoices.l10n_in_type_id.code,
|
||||
"transactionType": get_transaction_type(seller_details, dispatch_details, buyer_details, ship_to_details),
|
||||
"transDistance": str(invoices.l10n_in_distance),
|
||||
"docNo": invoices.is_purchase_document(include_receipts=True) and invoices.ref or invoices.name,
|
||||
"docDate": invoices.date.strftime("%d/%m/%Y"),
|
||||
"fromGstin": seller_details.commercial_partner_id.vat or "URP",
|
||||
"fromTrdName": seller_details.commercial_partner_id.name,
|
||||
"fromAddr1": dispatch_details.street or "",
|
||||
"fromAddr2": dispatch_details.street2 or "",
|
||||
"fromPlace": dispatch_details.city or "",
|
||||
"fromPincode": dispatch_details.country_id.code == "IN" and int(extract_digits(dispatch_details.zip)) or "",
|
||||
"fromStateCode": int(seller_details.state_id.l10n_in_tin) or "",
|
||||
"actFromStateCode": dispatch_details.state_id.l10n_in_tin and int(dispatch_details.state_id.l10n_in_tin) or "",
|
||||
"toGstin": buyer_details.commercial_partner_id.vat or "URP",
|
||||
"toTrdName": buyer_details.commercial_partner_id.name,
|
||||
"toAddr1": ship_to_details.street or "",
|
||||
"toAddr2": ship_to_details.street2 or "",
|
||||
"toPlace": ship_to_details.city or "",
|
||||
"toPincode": int(extract_digits(ship_to_details.zip)),
|
||||
"actToStateCode": int(ship_to_details.state_id.l10n_in_tin),
|
||||
"toStateCode": invoices.l10n_in_state_id.l10n_in_tin and int(invoices.l10n_in_state_id.l10n_in_tin) or (
|
||||
buyer_details.state_id.l10n_in_tin or int(buyer_details.state_id.l10n_in_tin) or ""
|
||||
),
|
||||
"itemList": [
|
||||
self._get_l10n_in_edi_ewaybill_line_details(line, line_tax_details, sign)
|
||||
for line, line_tax_details in invoice_line_tax_details.items()
|
||||
],
|
||||
"totalValue": self._l10n_in_round_value(tax_details.get("base_amount")),
|
||||
"cgstValue": self._l10n_in_round_value(tax_details_by_code.get("cgst_amount", 0.00)),
|
||||
"sgstValue": self._l10n_in_round_value(tax_details_by_code.get("sgst_amount", 0.00)),
|
||||
"igstValue": self._l10n_in_round_value(tax_details_by_code.get("igst_amount", 0.00)),
|
||||
"cessValue": self._l10n_in_round_value(tax_details_by_code.get("cess_amount", 0.00)),
|
||||
"cessNonAdvolValue": self._l10n_in_round_value(tax_details_by_code.get("cess_non_advol_amount", 0.00)),
|
||||
"otherValue": self._l10n_in_round_value(tax_details_by_code.get("other_amount", 0.00) + rounding_amount),
|
||||
"totInvValue": self._l10n_in_round_value(tax_details.get("base_amount") + tax_details.get("tax_amount") + rounding_amount),
|
||||
}
|
||||
is_overseas = invoices.l10n_in_gst_treatment in ("overseas", "special_economic_zone")
|
||||
if invoices.is_outbound():
|
||||
if is_overseas:
|
||||
json_payload.update({"fromStateCode": 99})
|
||||
if is_overseas and dispatch_details.state_id.country_id.code != "IN":
|
||||
json_payload.update({
|
||||
"actFromStateCode": 99,
|
||||
"fromPincode": 999999,
|
||||
})
|
||||
else:
|
||||
json_payload.update({
|
||||
"actFromStateCode": dispatch_details.state_id.l10n_in_tin and int(dispatch_details.state_id.l10n_in_tin) or "",
|
||||
"fromPincode": int(extract_digits(dispatch_details.zip)),
|
||||
})
|
||||
else:
|
||||
if is_overseas:
|
||||
json_payload.update({"toStateCode": 99})
|
||||
if is_overseas and ship_to_details.state_id.country_id.code != "IN":
|
||||
json_payload.update({
|
||||
"actToStateCode": 99,
|
||||
"toPincode": 999999,
|
||||
})
|
||||
else:
|
||||
json_payload.update({
|
||||
"actToStateCode": int(ship_to_details.state_id.l10n_in_tin),
|
||||
"toPincode": int(extract_digits(ship_to_details.zip)),
|
||||
})
|
||||
|
||||
if invoices.l10n_in_mode == "0":
|
||||
json_payload.update({
|
||||
"transporterId": invoices.l10n_in_transporter_id.vat or "",
|
||||
"transporterName": invoices.l10n_in_transporter_id.name or "",
|
||||
})
|
||||
if invoices.l10n_in_mode in ("2", "3", "4"):
|
||||
json_payload.update({
|
||||
"transMode": invoices.l10n_in_mode,
|
||||
"transDocNo": invoices.l10n_in_transportation_doc_no or "",
|
||||
"transDocDate": invoices.l10n_in_transportation_doc_date and
|
||||
invoices.l10n_in_transportation_doc_date.strftime("%d/%m/%Y") or "",
|
||||
})
|
||||
if invoices.l10n_in_mode == "1":
|
||||
json_payload.update({
|
||||
"transMode": invoices.l10n_in_mode,
|
||||
"vehicleNo": invoices.l10n_in_vehicle_no or "",
|
||||
"vehicleType": invoices.l10n_in_vehicle_type or "",
|
||||
})
|
||||
return json_payload
|
||||
|
||||
def _get_l10n_in_edi_ewaybill_line_details(self, line, line_tax_details, sign):
|
||||
extract_digits = self._l10n_in_edi_extract_digits
|
||||
tax_details_by_code = self._get_l10n_in_tax_details_by_line_code(line_tax_details.get("tax_details", {}))
|
||||
line_details = {
|
||||
"productName": line.product_id.name,
|
||||
"hsnCode": extract_digits(line.product_id.l10n_in_hsn_code),
|
||||
"productDesc": line.name,
|
||||
"quantity": line.quantity,
|
||||
"qtyUnit": line.product_uom_id.l10n_in_code and line.product_uom_id.l10n_in_code.split("-")[0] or "OTH",
|
||||
"taxableAmount": self._l10n_in_round_value(line.balance * sign),
|
||||
}
|
||||
gst_types = {'cgst', 'sgst', 'igst'}
|
||||
gst_tax_rates = {
|
||||
f"{gst_type}Rate": self._l10n_in_round_value(tax_details_by_code[f"{gst_type}_rate"])
|
||||
for gst_type in gst_types
|
||||
if tax_details_by_code.get(f"{gst_type}_rate")
|
||||
}
|
||||
line_details.update(
|
||||
gst_tax_rates or dict.fromkeys({f"{gst_type}Rate" for gst_type in gst_types}, 0.00)
|
||||
)
|
||||
if tax_details_by_code.get("cess_rate"):
|
||||
line_details.update({"cessRate": self._l10n_in_round_value(tax_details_by_code.get("cess_rate"))})
|
||||
return line_details
|
||||
|
||||
#================================ E-invoice API methods ===========================
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_irn_ewaybill_generate(self, company, json_payload):
|
||||
# IRN is created by E-invoice API call so waiting for it.
|
||||
if not json_payload.get("Irn"):
|
||||
return {"error": [{
|
||||
"code": "waiting",
|
||||
"message": _("waiting For IRN generation To create E-waybill")}
|
||||
]}
|
||||
token = self._l10n_in_edi_get_token(company)
|
||||
if not token:
|
||||
return self._l10n_in_edi_no_config_response()
|
||||
params = {
|
||||
"auth_token": token,
|
||||
"json_payload": json_payload,
|
||||
}
|
||||
return self._l10n_in_edi_connect_to_server(company, url_path="/iap/l10n_in_edi/1/generate_ewaybill_by_irn", params=params)
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_irn_ewaybill_get(self, company, irn):
|
||||
token = self._l10n_in_edi_get_token(company)
|
||||
if not token:
|
||||
return self._l10n_in_edi_no_config_response()
|
||||
params = {
|
||||
"auth_token": token,
|
||||
"irn": irn,
|
||||
}
|
||||
return self._l10n_in_edi_connect_to_server(company, url_path="/iap/l10n_in_edi/1/get_ewaybill_by_irn", params=params)
|
||||
|
||||
#=============================== E-waybill API methods ===================================
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_ewaybill_no_config_response(self):
|
||||
return {"error": [{
|
||||
"code": "0",
|
||||
"message": _(
|
||||
"Unable to send E-waybill."
|
||||
"Create an API user in NIC portal, and set it using the top menu: Configuration > Settings."
|
||||
)}
|
||||
]}
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_ewaybill_check_authentication(self, company):
|
||||
sudo_company = company.sudo()
|
||||
if sudo_company.l10n_in_edi_ewaybill_username and sudo_company._l10n_in_edi_ewaybill_token_is_valid():
|
||||
return True
|
||||
elif sudo_company.l10n_in_edi_ewaybill_username and sudo_company.l10n_in_edi_ewaybill_password:
|
||||
authenticate_response = self._l10n_in_edi_ewaybill_authenticate(company)
|
||||
if not authenticate_response.get("error"):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _l10n_in_set_missing_error_message(self, response):
|
||||
for error in response.get('error', []):
|
||||
if error.get('code') and not error.get('message'):
|
||||
error['message'] = self._l10n_in_edi_ewaybill_get_error_message(error.get('code'))
|
||||
return response
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_ewaybill_connect_to_server(self, company, url_path, params):
|
||||
user_token = self.env["iap.account"].get("l10n_in_edi")
|
||||
params.update({
|
||||
"account_token": user_token.account_token,
|
||||
"dbuuid": self.env["ir.config_parameter"].sudo().get_param("database.uuid"),
|
||||
"username": company.sudo().l10n_in_edi_ewaybill_username,
|
||||
"gstin": company.vat,
|
||||
})
|
||||
if company.sudo().l10n_in_edi_production_env:
|
||||
default_endpoint = DEFAULT_IAP_ENDPOINT
|
||||
else:
|
||||
default_endpoint = DEFAULT_IAP_TEST_ENDPOINT
|
||||
endpoint = self.env["ir.config_parameter"].sudo().get_param("l10n_in_edi_ewaybill.endpoint", default_endpoint)
|
||||
url = "%s%s" % (endpoint, url_path)
|
||||
try:
|
||||
response = jsonrpc(url, params=params, timeout=70)
|
||||
return self._l10n_in_set_missing_error_message(response)
|
||||
except AccessError as e:
|
||||
_logger.warning("Connection error: %s", e.args[0])
|
||||
return {
|
||||
"error": [{
|
||||
"code": "access_error",
|
||||
"message": _("Unable to connect to the E-WayBill service."
|
||||
"The web service may be temporary down. Please try again in a moment.")
|
||||
}]
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_ewaybill_authenticate(self, company):
|
||||
params = {"password": company.sudo().l10n_in_edi_ewaybill_password}
|
||||
response = self._l10n_in_edi_ewaybill_connect_to_server(
|
||||
company, url_path="/iap/l10n_in_edi_ewaybill/1/authenticate", params=params
|
||||
)
|
||||
if response and response.get("status_cd") == "1":
|
||||
company.sudo().l10n_in_edi_ewaybill_auth_validity = fields.Datetime.now() + timedelta(
|
||||
hours=6, minutes=00, seconds=00)
|
||||
return response
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_ewaybill_generate(self, company, json_payload):
|
||||
is_authenticated = self._l10n_in_edi_ewaybill_check_authentication(company)
|
||||
if not is_authenticated:
|
||||
return self._l10n_in_edi_ewaybill_no_config_response()
|
||||
params = {"json_payload": json_payload}
|
||||
return self._l10n_in_edi_ewaybill_connect_to_server(
|
||||
company, url_path="/iap/l10n_in_edi_ewaybill/1/generate", params=params
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_ewaybill_cancel(self, company, json_payload):
|
||||
is_authenticated = self._l10n_in_edi_ewaybill_check_authentication(company)
|
||||
if not is_authenticated:
|
||||
return self._l10n_in_edi_ewaybill_no_config_response()
|
||||
params = {"json_payload": json_payload}
|
||||
return self._l10n_in_edi_ewaybill_connect_to_server(
|
||||
company, url_path="/iap/l10n_in_edi_ewaybill/1/cancel", params=params
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_ewaybill_get_by_consigner(self, company, document_type, document_number):
|
||||
is_authenticated = self._l10n_in_edi_ewaybill_check_authentication(company)
|
||||
if not is_authenticated:
|
||||
return self._l10n_in_edi_ewaybill_no_config_response()
|
||||
params = {"document_type": document_type, "document_number": document_number}
|
||||
return self._l10n_in_edi_ewaybill_connect_to_server(
|
||||
company, url_path="/iap/l10n_in_edi_ewaybill/1/getewaybillgeneratedbyconsigner", params=params
|
||||
)
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
from odoo import fields, models, api, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
# Transaction Details
|
||||
l10n_in_type_id = fields.Many2one("l10n.in.ewaybill.type", "E-waybill Document Type", tracking=True)
|
||||
|
||||
# transportation details
|
||||
l10n_in_distance = fields.Integer("Distance", tracking=True)
|
||||
l10n_in_mode = fields.Selection([
|
||||
("0", "Managed by Transporter"),
|
||||
("1", "Road"),
|
||||
("2", "Rail"),
|
||||
("3", "Air"),
|
||||
("4", "Ship")],
|
||||
string="Transportation Mode", copy=False, tracking=True)
|
||||
|
||||
# Vehicle Number and Type required when transportation mode is By Road.
|
||||
l10n_in_vehicle_no = fields.Char("Vehicle Number", copy=False, tracking=True)
|
||||
l10n_in_vehicle_type = fields.Selection([
|
||||
("R", "Regular"),
|
||||
("O", "Over Dimensional Cargo")],
|
||||
string="Vehicle Type", copy=False, tracking=True)
|
||||
|
||||
# Document number and date required in case of transportation mode is Rail, Air or Ship.
|
||||
l10n_in_transportation_doc_no = fields.Char(
|
||||
string="E-waybill Document Number",
|
||||
help="""Transport document number. If it is more than 15 chars, last 15 chars may be entered""",
|
||||
copy=False, tracking=True)
|
||||
l10n_in_transportation_doc_date = fields.Date(
|
||||
string="Document Date",
|
||||
help="Date on the transporter document",
|
||||
copy=False,
|
||||
tracking=True)
|
||||
|
||||
# transporter id required when transportation done by other party.
|
||||
l10n_in_transporter_id = fields.Many2one("res.partner", "Transporter", copy=False, tracking=True)
|
||||
# show and hide fields base on this
|
||||
l10n_in_edi_ewaybill_direct_api = fields.Boolean(string="E-waybill(IN) direct API", compute="_compute_l10n_in_edi_ewaybill_direct")
|
||||
l10n_in_edi_ewaybill_show_send_button = fields.Boolean(string="Show Send E-waybill Button", compute="_compute_l10n_in_edi_ewaybill_show_send_button")
|
||||
|
||||
@api.depends('state', 'edi_document_ids', 'edi_document_ids.state')
|
||||
def _compute_l10n_in_edi_ewaybill_show_send_button(self):
|
||||
edi_format = self.env.ref('l10n_in_edi_ewaybill.edi_in_ewaybill_json_1_03')
|
||||
posted_moves = self.filtered(lambda x: x.is_invoice() and x.state == 'posted' and x.country_code == "IN")
|
||||
for move in posted_moves:
|
||||
already_sent = move.edi_document_ids.filtered(lambda x: x.edi_format_id == edi_format and x.state in ('sent', 'to_cancel', 'to_send'))
|
||||
if already_sent:
|
||||
move.l10n_in_edi_ewaybill_show_send_button = False
|
||||
else:
|
||||
move.l10n_in_edi_ewaybill_show_send_button = True
|
||||
(self - posted_moves).l10n_in_edi_ewaybill_show_send_button = False
|
||||
|
||||
@api.depends("l10n_in_gst_treatment")
|
||||
def _compute_l10n_in_edi_ewaybill_direct(self):
|
||||
for move in self:
|
||||
base = self.env["account.edi.format"]._l10n_in_edi_ewaybill_base_irn_or_direct(move)
|
||||
move.l10n_in_edi_ewaybill_direct_api = base == "direct"
|
||||
|
||||
@api.depends("edi_document_ids")
|
||||
def _compute_l10n_in_edi_show_cancel(self):
|
||||
super()._compute_l10n_in_edi_show_cancel()
|
||||
for invoice in self:
|
||||
if invoice.edi_document_ids.filtered(lambda i: i.edi_format_id.code == "in_ewaybill_1_03" and i.state in ("sent", "to_cancel", "cancelled")):
|
||||
invoice.l10n_in_edi_show_cancel = True
|
||||
|
||||
def _get_l10n_in_edi_ewaybill_response_json(self):
|
||||
self.ensure_one()
|
||||
l10n_in_edi = self.edi_document_ids.filtered(lambda i: i.edi_format_id.code == "in_ewaybill_1_03"
|
||||
and i.state in ("sent", "to_cancel"))
|
||||
if l10n_in_edi and l10n_in_edi.sudo().attachment_id:
|
||||
return json.loads(l10n_in_edi.sudo().attachment_id.raw.decode("utf-8"))
|
||||
else:
|
||||
return {}
|
||||
|
||||
def button_cancel_posted_moves(self):
|
||||
"""Mark the edi.document related to this move to be canceled."""
|
||||
reason_and_remarks_not_set = self.env["account.move"]
|
||||
for move in self:
|
||||
send_l10n_in_edi_ewaybill = move.edi_document_ids.filtered(lambda doc: doc.edi_format_id.code == "in_ewaybill_1_03")
|
||||
# check submitted E-waybill does not have reason and remarks
|
||||
# because it's needed to cancel E-waybill
|
||||
if send_l10n_in_edi_ewaybill and (not move.l10n_in_edi_cancel_reason or not move.l10n_in_edi_cancel_remarks):
|
||||
reason_and_remarks_not_set += move
|
||||
if reason_and_remarks_not_set:
|
||||
raise UserError(_(
|
||||
"To cancel E-waybill set cancel reason and remarks at E-waybill tab in: \n%s",
|
||||
("\n".join(reason_and_remarks_not_set.mapped("name"))),
|
||||
))
|
||||
return super().button_cancel_posted_moves()
|
||||
|
||||
def l10n_in_edi_ewaybill_send(self):
|
||||
edi_format = self.env.ref('l10n_in_edi_ewaybill.edi_in_ewaybill_json_1_03')
|
||||
edi_document_vals_list = []
|
||||
for move in self:
|
||||
if move.state != 'posted':
|
||||
raise UserError(_("You can only create E-waybill from posted invoice"))
|
||||
errors = edi_format._check_move_configuration(move)
|
||||
if errors:
|
||||
raise UserError(_("Invalid invoice configuration:\n\n%s") % '\n'.join(errors))
|
||||
existing_edi_document = move.edi_document_ids.filtered(lambda x: x.edi_format_id == edi_format)
|
||||
if existing_edi_document:
|
||||
if existing_edi_document.state in ('sent', 'to_cancel'):
|
||||
raise UserError(_("E-waybill is already created") % '\n'.join(errors))
|
||||
existing_edi_document.sudo().write({
|
||||
'state': 'to_send',
|
||||
'attachment_id': False,
|
||||
})
|
||||
else:
|
||||
edi_document_vals_list.append({
|
||||
'edi_format_id': edi_format.id,
|
||||
'move_id': move.id,
|
||||
'state': 'to_send',
|
||||
})
|
||||
self.env['account.edi.document'].create(edi_document_vals_list)
|
||||
self.env.ref('account_edi.ir_cron_edi_network')._trigger()
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _lt
|
||||
|
||||
ERROR_CODES = {
|
||||
"100": _lt("Invalid json"),
|
||||
"101": _lt("Invalid Username"),
|
||||
"102": _lt("Invalid Password"),
|
||||
"103": _lt("Invalid Client -Id"),
|
||||
"104": _lt("Invalid Client -Id"),
|
||||
"105": _lt("Invalid Token"),
|
||||
"106": _lt("Token Expired"),
|
||||
"107": _lt("Authentication failed. Pls. inform the helpdesk"),
|
||||
"108": _lt("Invalid login credentials."),
|
||||
"109": _lt("Decryption of data failed"),
|
||||
"110": _lt("Invalid Client-ID/Client-Secret"),
|
||||
"111": _lt("GSTIN is not registerd to this GSP"),
|
||||
"112": _lt("IMEI does not belong to the user"),
|
||||
"113": _lt("operating-system-type is mandatory in header"),
|
||||
"114": _lt("Invalid operating-system-type parameter value"),
|
||||
"117": _lt("This option is not enabled in Eway Bill2"),
|
||||
"118": _lt("Try after 5 minutes"),
|
||||
"201": _lt("Invalid Supply Type"),
|
||||
"202": _lt("Invalid Sub-supply Type"),
|
||||
"203": _lt("Sub-transaction type does not belongs to transaction type"),
|
||||
"204": _lt("Invalid Document type"),
|
||||
"205": _lt("Document type does not match with transaction & Sub trans type"),
|
||||
"206": _lt("Invaild Invoice Number"),
|
||||
"207": _lt("Invalid Invoice Date"),
|
||||
"208": _lt("Invalid Supplier GSTIN"),
|
||||
"209": _lt("Blank Supplier Address"),
|
||||
"210": _lt("Invalid or Blank Supplier PIN Code"),
|
||||
"211": _lt("Invalid or Blank Supplier state Code"),
|
||||
"212": _lt("Invalid Consignee GSTIN"),
|
||||
"213": _lt("Invalid Consignee Address"),
|
||||
"214": _lt("Invalid Consignee PIN Code"),
|
||||
"215": _lt("Invalid Consignee State Code"),
|
||||
"216": _lt("Invalid HSN Code"),
|
||||
"217": _lt("Invalid UQC Code"),
|
||||
"218": _lt("Invalid Tax Rate for Intra State Transaction"),
|
||||
"219": _lt("Invalid Tax Rate for Inter State Transaction"),
|
||||
"220": _lt("Invalid Trans mode"),
|
||||
"221": _lt("Invalid Approximate Distance"),
|
||||
"222": _lt("Invalid Transporter Id"),
|
||||
"223": _lt("Invalid Transaction Document Number"),
|
||||
"224": _lt("Invalid Transaction Date"),
|
||||
"225": _lt("Invalid Vehicle Number Format"),
|
||||
"226": _lt("Both Transaction and Vehicle Number Blank"),
|
||||
"227": _lt("User Gstin cannot be blank"),
|
||||
"228": _lt("User id cannot be blank"),
|
||||
"229": _lt("Supplier name is required"),
|
||||
"230": _lt("Supplier place is required"),
|
||||
"231": _lt("Consignee name is required"),
|
||||
"232": _lt("Consignee place is required"),
|
||||
"233": _lt("Eway bill does not contains any items"),
|
||||
"234": _lt("Total amount/Taxable amout is mandatory"),
|
||||
"235": _lt("Tax rates for Intra state transaction is blank"),
|
||||
"236": _lt("Tax rates for Inter state transaction is blank"),
|
||||
"237": _lt("Invalid client -Id/client-secret"),
|
||||
"238": _lt("Invalid auth token"),
|
||||
"239": _lt("Invalid action"),
|
||||
"240": _lt("Could not generate eway bill, pls contact helpdesk"),
|
||||
"242": _lt("Invalid or Blank Officer StateCode"),
|
||||
"243": _lt("Invalid or Blank IR Number"),
|
||||
"244": _lt("Invalid or Blank Actual Vehicle Number Format"),
|
||||
"245": _lt("Invalid Verification Date Format"),
|
||||
"246": _lt("Invalid Vehicle Release Date Format"),
|
||||
"247": _lt("Invalid Verification Time Format"),
|
||||
"248": _lt("Invalid Vehicle Release Date Format"),
|
||||
"249": _lt("Actual Value cannot be less than or equal to zero"),
|
||||
"250": _lt("Invalid Vehicle Release Date Format"),
|
||||
"251": _lt("CGST nad SGST TaxRate should be same"),
|
||||
"252": _lt("Invalid CGST Tax Rate"),
|
||||
"253": _lt("Invalid SGST Tax Rate"),
|
||||
"254": _lt("Invalid IGST Tax Rate"),
|
||||
"255": _lt("Invalid CESS Rate"),
|
||||
"256": _lt("Invalid Cess Non Advol value"),
|
||||
"278": _lt("User Gstin does not match with Transporter Id"),
|
||||
"280": _lt("Status is not ACTIVE"),
|
||||
"281": _lt("Eway Bill is already expired hence update transporter is not allowed."),
|
||||
"301": _lt("Invalid eway bill number"),
|
||||
"302": _lt("Invalid transporter mode"),
|
||||
"303": _lt("Vehicle number is required"),
|
||||
"304": _lt("Invalid vehicle format"),
|
||||
"305": _lt("Place from is required"),
|
||||
"306": _lt("Invalid from state"),
|
||||
"307": _lt("Invalid reason"),
|
||||
"308": _lt("Invalid remarks"),
|
||||
"309": _lt("Could not update vehicle details, pl contact helpdesk"),
|
||||
"311": _lt("Validity period lapsed, you cannot update vehicle details"),
|
||||
"312": _lt("This eway bill is either not generated by you or cancelled"),
|
||||
"313": _lt("Error in validating ewaybill for vehicle updation"),
|
||||
"315": _lt("Validity period lapsed, you cannot cancel this eway bill"),
|
||||
"316": _lt("Eway bill is already verified, you cannot cancel it"),
|
||||
"317": _lt("Could not cancel eway bill, please contact helpdesk"),
|
||||
"320": _lt("Invalid state to"),
|
||||
"321": _lt("Invalid place to"),
|
||||
"322": _lt("Could not generate consolidated eway bill"),
|
||||
"325": _lt("Could not retrieve data"),
|
||||
"326": _lt("Could not retrieve GSTIN details for the given GSTIN number"),
|
||||
"327": _lt("Could not retrieve data from hsn"),
|
||||
"328": _lt("Could not retrieve transporter details from gstin"),
|
||||
"329": _lt("Could not retrieve States List"),
|
||||
"330": _lt("Could not retrieve UQC list"),
|
||||
"331": _lt("Could not retrieve Error code"),
|
||||
"334": _lt("Could not retrieve user details by userid "),
|
||||
"336": _lt("Could not retrieve transporter data by gstin "),
|
||||
"337": _lt("Could not retrieve HSN details for the given HSN number"),
|
||||
"338": _lt("You cannot update transporter details, as the current tranporter is already entered Part B details of the eway bill"),
|
||||
"339": _lt("You are not assigned to update the tranporter details of this eway bill"),
|
||||
"341": _lt("This e-way bill is generated by you and hence you cannot reject it"),
|
||||
"342": _lt("You cannot reject this e-way bill as you are not the other party to do so"),
|
||||
"343": _lt("This e-way bill is cancelled"),
|
||||
"344": _lt("Invalid eway bill number"),
|
||||
"345": _lt("Validity period lapsed, you cannot reject the e-way bill"),
|
||||
"346": _lt("You can reject the e-way bill only within 72 hours from generated time"),
|
||||
"347": _lt("Validation of eway bill number failed, while rejecting ewaybill"),
|
||||
"348": _lt("Part-B is not generated for this e-way bill, hence rejection is not allowed."),
|
||||
"350": _lt("Could not generate consolidated eway bill"),
|
||||
"351": _lt("Invalid state code"),
|
||||
"352": _lt("Invalid rfid date"),
|
||||
"353": _lt("Invalid location code"),
|
||||
"354": _lt("Invalid rfid number"),
|
||||
"355": _lt("Invalid Vehicle Number Format"),
|
||||
"356": _lt("Invalid wt on bridge"),
|
||||
"357": _lt("Could not retrieve eway bill details, pl. contact helpdesk"),
|
||||
"358": _lt("GSTIN passed in request header is not matching with the user gstin mentioned in payload JSON"),
|
||||
"359": _lt("User GSTIN should match to GSTIN(from) for outward transactions"),
|
||||
"360": _lt("User GSTIN should match to GSTIN(to) for inward transactions"),
|
||||
"361": _lt("Invalid Vehicle Type"),
|
||||
"362": _lt("Transporter document date cannot be earlier than the invoice date"),
|
||||
"363": _lt("E-way bill is not enabled for intra state movement for you state"),
|
||||
"364": _lt("Error in verifying eway bill"),
|
||||
"365": _lt("Error in verifying consolidated eway bill"),
|
||||
"366": _lt("You will not get the ewaybills generated today, howerver you cann access the ewaybills of yester days"),
|
||||
"367": _lt("Could not retrieve data for officer login"),
|
||||
"368": _lt("Could not update transporter"),
|
||||
"369": _lt("GSTIN/Transin passed in request header should match with the transported Id mentioned in payload JSON"),
|
||||
"370": _lt("GSTIN/Transin passed in request header should not be the same as supplier(fromGSTIN) or recepient(toGSTIN)"),
|
||||
"371": _lt("Invalid or Blank Supplier Ship-to State Code"),
|
||||
"372": _lt("Invalid or Blank Consignee Ship-to State Code"),
|
||||
"373": _lt("The Supplier ship-to state code should be Other Country for Sub Supply Type- Export"),
|
||||
"374": _lt("The Consignee pin code should be 999999 for Sub Supply Type- Export"),
|
||||
"375": _lt("The Supplier ship-from state code should be Other Country for Sub Supply Type- Import"),
|
||||
"376": _lt("The Supplier pin code should be 999999 for Sub Supply Type- Import"),
|
||||
"377": _lt("Sub Supply Type is mentioned as Others, the description for that is mandatory"),
|
||||
"378": _lt("The supplier or conginee belong to SEZ, Inter state tax rates are applicable here"),
|
||||
"379": _lt("Eway Bill can not be extended.. Already Cancelled"),
|
||||
"380": _lt("Eway Bill Can not be Extended. Not in Active State"),
|
||||
"381": _lt("There is No PART-B/Vehicle Entry.. So Please Update Vehicle Information.."),
|
||||
"382": _lt("You Cannot Extend as EWB can be Extended only 8 hour before or after w.r.t Validity of EWB..!!"),
|
||||
"383": _lt("Error While Extending..Please Contact Helpdesk. "),
|
||||
"384": _lt("You are not current transporter or Generator of the ewayBill, with no transporter details."),
|
||||
"385": _lt("For Rail/Ship/Air transDocDate is mandatory"),
|
||||
"386": _lt("Reason Code, Remarks is mandatory."),
|
||||
"387": _lt("No Record Found for Entered consolidated eWay bill."),
|
||||
"388": _lt("Exception in regenration of consolidated eWayBill!!Please Contact helpdesk"),
|
||||
"389": _lt("Remaining Distance Required"),
|
||||
"390": _lt("Remaining Distance Can not be greater than Actual Distance."),
|
||||
"391": _lt("No eway bill of specified tripsheet, neither ACTIVE nor not Valid."),
|
||||
"392": _lt("Tripsheet is already cancelled, Hence Regeration is not possible"),
|
||||
"393": _lt("Invalid GSTIN"),
|
||||
"394": _lt("For other than Road Transport, TransDoc number is required"),
|
||||
"395": _lt("Eway Bill Number should be numeric only"),
|
||||
"396": _lt("Either Eway Bill Number Or Consolidated Eway Bill Number is required for Verification"),
|
||||
"397": _lt("Error in Multi Vehicle Movement Initiation"),
|
||||
"398": _lt("Eway Bill Item List is Empty"),
|
||||
"399": _lt("Unit Code is not matching with any of the Unit Code from eway bill ItemList"),
|
||||
"400": _lt("total quantity is exceeding from multi vehicle movement initiation quantity"),
|
||||
"401": _lt("Error in inserting multi vehicle details"),
|
||||
"402": _lt("total quantity can not be less than or equal to zero"),
|
||||
"403": _lt("Error in multi vehicle details"),
|
||||
"405": _lt("No record found for multi vehicle update with specified ewbNo groupNo and old vehicleNo/transDocNo with status as ACT"),
|
||||
"406": _lt("Group number cannot be empty or zero"),
|
||||
"407": _lt("Invalid old vehicle number format"),
|
||||
"408": _lt("Invalid new vehicle number format"),
|
||||
"409": _lt("Invalid old transDoc number"),
|
||||
"410": _lt("Invalid new transDoc number"),
|
||||
"411": _lt("Multi Vehicle Initiation data is not there for specified ewayBill and group No"),
|
||||
"412": _lt("Multi Vehicle movement is already Initiated,hence PART B updation not allowed"),
|
||||
"413": _lt("Unit Code is not matching with unit code of first initiaton"),
|
||||
"415": _lt("Error in fetching in verification data for officer"),
|
||||
"416": _lt("Date range is exceeding allowed date range "),
|
||||
"417": _lt("No verification data found for officer "),
|
||||
"418": _lt("No record found"),
|
||||
"419": _lt("Error in fetching search result for taxpayer/transporter"),
|
||||
"420": _lt("Minimum six character required for Tradename/legalname search"),
|
||||
"421": _lt("Invalid pincode"),
|
||||
"422": _lt("Invalid mobile number"),
|
||||
"423": _lt("Error in fetching ewaybill list by vehicle number"),
|
||||
"424": _lt("Invalid PAN number"),
|
||||
"425": _lt("Error in fetching Part A data by IR Number"),
|
||||
"426": _lt("For Vehicle Released vehicle release date and time is mandatory"),
|
||||
"427": _lt("Error in saving Part-A verification Report"),
|
||||
"428": _lt("For Goods Detained,Vehicle Released feild is mandatory"),
|
||||
"429": _lt("Error in saving Part-B verification Report"),
|
||||
"430": _lt("Goods Detained Field required."),
|
||||
"431": _lt("Part-A for this ewaybill is already generated by you."),
|
||||
"432": _lt("invalid vehicle released value"),
|
||||
"433": _lt("invalid goods detained parameter value"),
|
||||
"434": _lt("invalid ewbNoAvailable parameter value"),
|
||||
"435": _lt("Part B is already updated,hence updation is not allowed"),
|
||||
"436": _lt("Invalid Consignee ship to State Code for the given pincode"),
|
||||
"437": _lt("Invalid Supplier ship from State Code for the given pincode"),
|
||||
"438": _lt("Invalid Latitude"),
|
||||
"439": _lt("Invalid Longitude"),
|
||||
"440": _lt("Error in inserting in verification data"),
|
||||
"441": _lt("Invalid verification type"),
|
||||
"442": _lt("Error in inserting verification details"),
|
||||
"443": _lt("invalid invoice available value"),
|
||||
"444": _lt("This Ewaybill cannot be cancelled as it is generated from NIC1"),
|
||||
"445": _lt("This Ewaybill cannot be cancelled as it is generated from NIC2"),
|
||||
"446": _lt("Transport details cannot be updated here as it is generated from NIC1"),
|
||||
"447": _lt("Transport details cannot be updated here as it is generated from NIC2"),
|
||||
"448": _lt("Part B cannot be updated as this Ewaybill Part A is generated in NIC1"),
|
||||
"449": _lt("Part B cannot be updated as this Ewaybill Part A is generated in NIC2"),
|
||||
"452": _lt("Consolidate Ewaybill cannot be generated as this Ewaybill Part A is generated in NIC2"),
|
||||
"600": _lt("Invalid category"),
|
||||
"601": _lt("Invalid date format"),
|
||||
"602": _lt("Invalid File Number"),
|
||||
"603": _lt("For file details file number is required"),
|
||||
"604": _lt("E-way bill(s) are already generated for the same document number, you cannot generate again on same document number"),
|
||||
"607": _lt("dispatch from gstin is mandatary "),
|
||||
"608": _lt("ship to from gstin is mandatary"),
|
||||
"609": _lt(" invalid ship to from gstin "),
|
||||
"610": _lt("invalid dispatch from gstin "),
|
||||
"611": _lt("invalid document type for the given supply type "),
|
||||
"612": _lt("Invalid transaction type"),
|
||||
"613": _lt("Exception in getting Officer Role"),
|
||||
"614": _lt("Transaction type is mandatory"),
|
||||
"615": _lt("Dispatch From GSTIN cannot be sent as the transaction type selected is Regular"),
|
||||
"616": _lt("Ship to GSTIN cannot be sent as the transaction type selected is Regular"),
|
||||
"617": _lt("Bill-from and dispatch-from gstin should not be same for this transaction type"),
|
||||
"618": _lt("Bill-to and ship-to gstin should not be same for this transaction type"),
|
||||
"619": _lt("Transporter Id is mandatory for generation of Part A slip"),
|
||||
"620": _lt("Total invoice value cannot be less than the sum of total assessible value and tax values"),
|
||||
"621": _lt("Transport mode is mandatory since vehicle number is present"),
|
||||
"622": _lt("Transport mode is mandatory since transport document number is present"),
|
||||
"623": _lt("IGST value is not applicable for Intra State Transaction"),
|
||||
"624": _lt("CGST/SGST value is not applicable for Inter State Transaction"),
|
||||
"627": _lt("Total value should not be negative"),
|
||||
"628": _lt("Total invoice value should not be negative"),
|
||||
"629": _lt("IGST value should not be negative"),
|
||||
"630": _lt("CGST value should not be negative"),
|
||||
"631": _lt("SGST value should not be negative"),
|
||||
"632": _lt("Cess value should not be negative"),
|
||||
"633": _lt("Cess non advol should not be negative"),
|
||||
"634": _lt("Vehicle type should not be ODC when transmode is other than road"),
|
||||
"635": _lt("You cannot update part B, as the current tranporter is already entered Part B details of the eway bill"),
|
||||
"636": _lt("You are not assigned to update part B"),
|
||||
"637": _lt("You cannot extend ewaybill, as the current tranporter is already entered Part B details of the ewaybill"),
|
||||
"638": _lt("Transport mode is mandatory as Vehicle Number/Transport Document Number is given"),
|
||||
"640": _lt("Tolal Invoice value is mandatory"),
|
||||
"641": _lt("For outward CKD/SKD/Lots supply type, Bill To state should be as Other Country, since the Bill To GSTIN given is of SEZ unit"),
|
||||
"642": _lt("For inward CKD/SKD/Lots supply type, Bill From state should be as Other Country, since the Bill From GSTIN given is of SEZ unit"),
|
||||
"643": _lt("For regular transaction, Bill from state code and Dispatch from state code should be same"),
|
||||
"644": _lt("For regular transaction, Bill to state code and Ship to state code should be same"),
|
||||
"645": _lt("You cannot do multivehicle movement, as the current tranporter is already entered Part B details of the ewaybill"),
|
||||
"646": _lt("You are not assigned to do MultiVehicle Movement"),
|
||||
"647": _lt("Could not insert RFID data, pl. contact helpdisk"),
|
||||
"648": _lt("Multi Vehicle movement is already Initiated,hence generation of consolidated eway bill is not allowed"),
|
||||
"649": _lt("You cannot generate consolidated eway bill , as the current tranporter is already entered Part B details of the eway bill"),
|
||||
"650": _lt("You are not assigned to generate consolidated ewaybill"),
|
||||
"651": _lt("For Category Part-A or Part-B ewbdt is mandatory"),
|
||||
"652": _lt("For Category EWB03 procdt is mandatory"),
|
||||
"654": _lt("This GSTIN has generated a common Enrolment Number. Hence you are not allowed to generate Eway bill"),
|
||||
"655": _lt("This GSTIN has generated a common Enrolment Number. Hence you cannot mention it as a tranporter"),
|
||||
"656": _lt("This Eway Bill does not belongs to your state"),
|
||||
"657": _lt("Eway Bill Category wise details will be available after 4 days only"),
|
||||
"658": _lt("You are blocked for accesing this API as the allowed number of requests has been exceeded for this duration"),
|
||||
"659": _lt("Remarks is mandatory"),
|
||||
"670": _lt("Invalid Month Parameter"),
|
||||
"671": _lt("Invalid Year Parameter"),
|
||||
"672": _lt("User Id is mandatory"),
|
||||
"673": _lt("Error in getting officer dashboard"),
|
||||
"675": _lt("Error in getting EWB03 details by acknowledgement date range"),
|
||||
"676": _lt("Error in getting EWB Not Available List by entered date range"),
|
||||
"677": _lt("Error in getting EWB Not Available List by closed date range"),
|
||||
"678": _lt("Invalid Uniq No"),
|
||||
"679": _lt("Invalid EWB03 Ack No"),
|
||||
"680": _lt("Invalid Close Reason"),
|
||||
"681": _lt("Error in Closing EWB Verification Data"),
|
||||
"682": _lt("No Record available to Close"),
|
||||
"683": _lt("Error in fetching WatchList Data"),
|
||||
"685": _lt("Exception in fetching dashboard data"),
|
||||
"700": _lt("You are not assigned to extend e-waybill"),
|
||||
"701": _lt("Invalid Vehicle Direction"),
|
||||
"702": _lt("The distance between the pincodes given is too high"),
|
||||
"703": _lt("Since the consignor is Composite Taxpayer, inter state transactions are not allowed"),
|
||||
"704": _lt("Since the consignor is Composite Taxpayer, Tax rates should be zero"),
|
||||
"705": _lt("Invalid transit type"),
|
||||
"706": _lt("Address Line1 is mandatory"),
|
||||
"707": _lt("Address Line2 is mandatory"),
|
||||
"708": _lt("Address Line3 is mandatory"),
|
||||
"709": _lt("Pin to pin distance is not available for the given pin codes"),
|
||||
"710": _lt("Invalid state code for the given pincode"),
|
||||
"711": _lt("Invalid consignment status for the given transmode"),
|
||||
"712": _lt("Transit Type is not required as the goods are in movement"),
|
||||
"713": _lt("Transit Address is not required as the goods are in movement"),
|
||||
"714": _lt("Document type - Tax Invoice is not allowed for composite tax payer"),
|
||||
"715": _lt("The Consignor GSTIN is blocked from e-waybill generation as Return is not filed for past 2 months"),
|
||||
"716": _lt("The Consignee GSTIN is blocked from e-waybill generation as Return is not filed for past 2 months"),
|
||||
"717": _lt("The Transporter GSTIN is blocked from e-waybill generation as Return is not filed for past 2 months"),
|
||||
"718": _lt("The User GSTIN is blocked from Transporter Updation as Return is not filed for past 2 months"),
|
||||
"719": _lt("The Transporter GSTIN is blocked from Transporter Updation as Return is not filed for past 2 months"),
|
||||
"720": _lt("E Way Bill should be generated as part of IRN generation or with reference to IRN in E Invoice System, Since Supplier is enabled for E Invoice."),
|
||||
"721": _lt("The distance between the given pincodes are not available in the system. Please provide distance."),
|
||||
"722": _lt("Consignee GSTIN is cancelled and document date is later than the De-Registration date"),
|
||||
"724": _lt("HSN code of at least one item should be of goods to generate e-Way Bill"),
|
||||
"726": _lt("Vehicle type can not be regular when transportation mode is ship"),
|
||||
"727": _lt("This e-Way Bill does not have Oxygen items"),
|
||||
"728": _lt("You can cancel the ewaybill within 24 hours from Part B entry"),
|
||||
"801": _lt("Transporter id is not required for ewaybill for gold"),
|
||||
"802": _lt("Transporter name is not required for ewaybill for gold"),
|
||||
"803": _lt("TransDocNo is not required for ewaybill for gold"),
|
||||
"804": _lt("TransDocDate is not required for ewaybill for gold"),
|
||||
"805": _lt("Vehicle No is not required for ewaybill for gold"),
|
||||
"806": _lt("Vehicle Type is not required for ewaybill for gold"),
|
||||
"807": _lt("Transmode is mandatory for ewaybill for gold"),
|
||||
"808": _lt("Inter-State ewaybill is not allowed for gold"),
|
||||
"809": _lt("Other items are not allowed with eway bill for gold"),
|
||||
"810": _lt("Transport can not be updated for EwayBill For Gold"),
|
||||
"811": _lt("Vehicle can not be updated for EwayBill For Gold"),
|
||||
"812": _lt("ConsolidatedEWB cannot be generated for EwayBill For Gold "),
|
||||
"813": _lt("Duplicate request at the same time"),
|
||||
"814": _lt("MultiVehicleMovement cannot be initiated for EWay Bill For Gold"),
|
||||
"815": _lt("Only trans mode road is allowed for Eway Bill For Gold"),
|
||||
"816": _lt("Only transmode road is allowed for extending ewaybill for gold"),
|
||||
"817": _lt("MultiVehicleMovement cannot be initiated.Eway Bill is not in Active State"),
|
||||
"818": _lt("Validity period lapsed.Cannot generate consolidated Eway Bill"),
|
||||
"819": _lt("Ewaybill cannot be generated for the document date which is prior to 01/07/2017"),
|
||||
"820": _lt("You cannot generate E Way Bill with document date earlier than 180 days"),
|
||||
"821": _lt("E Way Bill cannot be extended as allowed limit is 360 days"),
|
||||
"4043": _lt("You cannot generate E Way Bill with document date earlier than 180 days"),
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, _
|
||||
|
||||
|
||||
class EWayBillType(models.Model):
|
||||
_name = "l10n.in.ewaybill.type"
|
||||
_description = "E-Waybill Document Type"
|
||||
|
||||
name = fields.Char("Type")
|
||||
code = fields.Char("Type Code")
|
||||
sub_type = fields.Char("Sub-type")
|
||||
sub_type_code = fields.Char("Sub-type Code")
|
||||
allowed_supply_type = fields.Selection(
|
||||
[
|
||||
("both", "Incoming and Outgoing"),
|
||||
("out", "Outgoing"),
|
||||
("in", "Incoming"),
|
||||
],
|
||||
string="Allowed for supply type",
|
||||
)
|
||||
active = fields.Boolean("Active", default=True)
|
||||
|
||||
def name_get(self):
|
||||
"""Show name and sub_type in name"""
|
||||
result_dict = dict()
|
||||
for ewaybill_type in self:
|
||||
name = ewaybill_type.name
|
||||
name += _(" (Sub-Type: %s)", ewaybill_type.sub_type)
|
||||
result_dict[ewaybill_type.id] = name
|
||||
return list(result_dict.items())
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
l10n_in_edi_ewaybill_username = fields.Char("E-Waybill (IN) Username", groups="base.group_system")
|
||||
l10n_in_edi_ewaybill_password = fields.Char("E-Waybill (IN) Password", groups="base.group_system")
|
||||
l10n_in_edi_ewaybill_auth_validity = fields.Datetime("E-Waybill (IN) Valid Until", groups="base.group_system")
|
||||
|
||||
def _l10n_in_edi_ewaybill_token_is_valid(self):
|
||||
self.ensure_one()
|
||||
if self.l10n_in_edi_ewaybill_auth_validity and self.l10n_in_edi_ewaybill_auth_validity > fields.Datetime.now():
|
||||
return True
|
||||
return False
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, fields, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import html_escape
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
l10n_in_edi_ewaybill_username = fields.Char("Indian EDI Stock username",
|
||||
related="company_id.l10n_in_edi_ewaybill_username", readonly=False)
|
||||
l10n_in_edi_ewaybill_password = fields.Char("Indian EDI Stock password",
|
||||
related="company_id.l10n_in_edi_ewaybill_password", readonly=False)
|
||||
|
||||
def l10n_in_edi_ewaybill_test(self):
|
||||
self.l10n_in_check_gst_number()
|
||||
response = self.env["account.edi.format"]._l10n_in_edi_ewaybill_authenticate(self.company_id)
|
||||
if response.get("error") or not self.company_id.sudo()._l10n_in_edi_ewaybill_token_is_valid():
|
||||
error_message = _("Incorrect username or password, or the GST number on company does not match.")
|
||||
if response.get("error"):
|
||||
error_message = "\n".join(["[%s] %s" % (e.get("code"), html_escape(e.get("message"))) for e in response["error"]])
|
||||
raise UserError(error_message)
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'sticky': False,
|
||||
'message': _("API credentials validated successfully"),
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
l10n_in_edi_ewaybill.access_l10n_in_ewaybill_type,access_l10n_in_ewaybill_type,l10n_in_edi_ewaybill.model_l10n_in_ewaybill_type,base.group_user,1,0,0,0
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_edi_ewaybill_json
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo.addons.l10n_in_edi.tests.test_edi_json import TestEdiJson
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged("post_install_l10n", "post_install", "-at_install")
|
||||
class TestEdiEwaybillJson(TestEdiJson):
|
||||
|
||||
def test_edi_json(self):
|
||||
self.env['account.move'].browse((
|
||||
self.invoice.id,
|
||||
self.invoice_full_discount.id,
|
||||
self.invoice_zero_qty.id,
|
||||
self.invoice_reverse.id,
|
||||
)).write({
|
||||
"l10n_in_type_id": self.env.ref("l10n_in_edi_ewaybill.type_tax_invoice_sub_type_supply"),
|
||||
"l10n_in_distance": 20,
|
||||
"l10n_in_mode": "1",
|
||||
"l10n_in_vehicle_no": "GJ11AA1234",
|
||||
"l10n_in_vehicle_type": "R",
|
||||
})
|
||||
json_value = self.env["account.edi.format"]._l10n_in_edi_ewaybill_generate_json(self.invoice)
|
||||
expected = {
|
||||
"supplyType": "O",
|
||||
"docType": "INV",
|
||||
"subSupplyType": "1",
|
||||
"transactionType": 1,
|
||||
"transDistance": "20",
|
||||
"transMode": "1",
|
||||
"vehicleNo": "GJ11AA1234",
|
||||
"vehicleType": "R",
|
||||
"docNo": "INV/2019/00001",
|
||||
"docDate": "01/01/2019",
|
||||
"fromGstin": "36AABCT1332L011",
|
||||
"fromTrdName": "company_1_data",
|
||||
"fromAddr1": "Block no. 401",
|
||||
"fromAddr2": "Street 2",
|
||||
"fromPlace": "City 1",
|
||||
"fromPincode": 500001,
|
||||
"fromStateCode": 36,
|
||||
"actFromStateCode": 36,
|
||||
"toGstin": "36BBBFF5679L8ZR",
|
||||
"toTrdName": "partner_a",
|
||||
"toAddr1": "Block no. 401",
|
||||
"toAddr2": "Street 2",
|
||||
"toPlace": "City 2",
|
||||
"toPincode": 500001,
|
||||
"actToStateCode": 36,
|
||||
"toStateCode": 36,
|
||||
"itemList": [
|
||||
{
|
||||
"productName": "product_a",
|
||||
"hsnCode": "01111",
|
||||
"productDesc": "product_a",
|
||||
"quantity": 1.0,
|
||||
"qtyUnit": "UNT",
|
||||
"taxableAmount": 900.0,
|
||||
"cgstRate": 2.5,
|
||||
"sgstRate": 2.5
|
||||
},
|
||||
{
|
||||
"productName": "product_with_cess",
|
||||
"hsnCode": "02222",
|
||||
"productDesc": "product_with_cess",
|
||||
"quantity": 1.0,
|
||||
"qtyUnit": "UNT",
|
||||
"taxableAmount": 900.0,
|
||||
"cgstRate": 6.0,
|
||||
"sgstRate": 6.0,
|
||||
"cessRate": 5.0
|
||||
}
|
||||
],
|
||||
"totalValue": 1800.0,
|
||||
"cgstValue": 76.5,
|
||||
"sgstValue": 76.5,
|
||||
"igstValue": 0.0,
|
||||
"cessValue": 45.0,
|
||||
"cessNonAdvolValue": 1.59,
|
||||
"otherValue": 0.0,
|
||||
"totInvValue": 1999.59
|
||||
}
|
||||
self.assertDictEqual(json_value, expected, "Indian EDI send json value is not matched")
|
||||
|
||||
# =================================== Different UOM Test ===========================================
|
||||
self.invoice.button_draft()
|
||||
self.invoice.invoice_line_ids.product_uom_id = self.env.ref('uom.product_uom_dozen')
|
||||
self.invoice.action_post()
|
||||
json_value = self.env["account.edi.format"]._l10n_in_edi_ewaybill_generate_json(self.invoice)
|
||||
self.assertListEqual(
|
||||
json_value['itemList'],
|
||||
[
|
||||
{
|
||||
"productName": "product_a",
|
||||
"hsnCode": "01111",
|
||||
"productDesc": "product_a",
|
||||
"quantity": 1.0,
|
||||
"qtyUnit": "DOZ",
|
||||
"taxableAmount": 900.0 * 12,
|
||||
"cgstRate": 2.5,
|
||||
"sgstRate": 2.5
|
||||
},
|
||||
{
|
||||
"productName": "product_with_cess",
|
||||
"hsnCode": "02222",
|
||||
"productDesc": "product_with_cess",
|
||||
"quantity": 1.0,
|
||||
"qtyUnit": "DOZ",
|
||||
"taxableAmount": 900.0 * 12,
|
||||
"cgstRate": 6.0,
|
||||
"sgstRate": 6.0,
|
||||
"cessRate": 5.0
|
||||
}
|
||||
],
|
||||
"Indian EDI send json UOM value is not matched"
|
||||
)
|
||||
|
||||
# =================================== Credit Note Test =============================================
|
||||
credit_note_expected = expected.copy()
|
||||
credit_note_expected.update({
|
||||
'docDate': '25/12/2023',
|
||||
'docNo': 'RINV/2023/00001',
|
||||
'supplyType': 'I',
|
||||
"fromGstin": expected['toGstin'],
|
||||
"fromTrdName": expected['toTrdName'],
|
||||
"fromAddr1": expected['toAddr1'],
|
||||
"fromAddr2": expected['toAddr2'],
|
||||
"fromPlace": expected['toPlace'],
|
||||
"fromPincode": expected['toPincode'],
|
||||
"fromStateCode": expected['toStateCode'],
|
||||
"actFromStateCode": expected['actToStateCode'],
|
||||
"toGstin": expected['fromGstin'],
|
||||
"toTrdName": expected['fromTrdName'],
|
||||
"toAddr1": expected['fromAddr1'],
|
||||
"toAddr2": expected['fromAddr2'],
|
||||
"toPlace": expected['fromPlace'],
|
||||
"toPincode": expected['fromPincode'],
|
||||
"toStateCode": expected['fromStateCode'],
|
||||
"actToStateCode": expected['actFromStateCode'],
|
||||
})
|
||||
self.assertDictEqual(
|
||||
self.env.ref(
|
||||
'l10n_in_edi_ewaybill.edi_in_ewaybill_json_1_03'
|
||||
)._l10n_in_edi_ewaybill_generate_json(self.invoice_reverse),
|
||||
credit_note_expected,
|
||||
)
|
||||
|
||||
#=================================== Full discount test =====================================
|
||||
json_value = self.env["account.edi.format"]._l10n_in_edi_ewaybill_generate_json(self.invoice_full_discount)
|
||||
expected.update({
|
||||
"docNo": "INV/2019/00002",
|
||||
"itemList": [{
|
||||
"productName": "product_a", "hsnCode": "01111", "productDesc": "product_a", "quantity": 1.0,
|
||||
"qtyUnit": "UNT", "taxableAmount": 0.0, "cgstRate": 0.0, "sgstRate": 0.0, 'igstRate': 0.0,
|
||||
}],
|
||||
"totalValue": 0.0,
|
||||
"cgstValue": 0.0,
|
||||
"sgstValue": 0.0,
|
||||
"igstValue": 0.0,
|
||||
"cessValue": 0.0,
|
||||
"cessNonAdvolValue": 0.00,
|
||||
"otherValue": 0.0,
|
||||
"totInvValue": 0.0
|
||||
})
|
||||
self.assertDictEqual(json_value, expected, "Indian EDI with 100% discount sent json value is not matched")
|
||||
|
||||
#=================================== Zero quantity test =============================================
|
||||
json_value = self.env["account.edi.format"]._l10n_in_edi_ewaybill_generate_json(self.invoice_zero_qty)
|
||||
expected.update({
|
||||
"docNo": "INV/2019/00003",
|
||||
"itemList": [{
|
||||
"productName": "product_a", "hsnCode": "01111", "productDesc": "product_a", "quantity": 0.0,
|
||||
"qtyUnit": "UNT", "taxableAmount": 0.0, "cgstRate": 0.0, "sgstRate": 0.0, 'igstRate': 0.0,
|
||||
}],
|
||||
"totalValue": 0.0,
|
||||
"cgstValue": 0.0,
|
||||
"sgstValue": 0.0,
|
||||
"igstValue": 0.0,
|
||||
"cessValue": 0.0,
|
||||
"cessNonAdvolValue": 0.00,
|
||||
"otherValue": 0.0,
|
||||
"totInvValue": 0.0
|
||||
})
|
||||
self.assertDictEqual(json_value, expected, "Indian EDI with 0(zero) quantity sent json value is not matched")
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="invoice_form_inherit_l10n_in_edi_ewaybill" model="ir.ui.view">
|
||||
<field name="name">account.move.form.inherit.l10n.in.ewaybill</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='button_set_checked']" position="after">
|
||||
<field name="l10n_in_edi_ewaybill_show_send_button" invisible="1"/>
|
||||
<button name="l10n_in_edi_ewaybill_send" string="Send E-waybill" class="oe_highlight" type="object" groups="account.group_account_invoice" attrs="{'invisible' : [('l10n_in_edi_ewaybill_show_send_button','!=',True)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//notebook/page[@name='other_info']" position="before">
|
||||
<page string="eWayBill" name="l10n_in_edi_ewaybill_page"
|
||||
attrs="{'invisible':['|', ('move_type', '=', 'entry'), ('country_code', '!=', 'IN')]}">
|
||||
<field name="l10n_in_edi_ewaybill_direct_api" invisible="1"/>
|
||||
<group name="ewaybill_group">
|
||||
<group string="Transaction Details" name="Transaction_group"
|
||||
attrs="{'invisible': [('l10n_in_edi_ewaybill_direct_api', '!=', True)]}">
|
||||
<field name="l10n_in_type_id" domain="[('allowed_supply_type', 'in', ('in', 'both')) if move_type in ('in_invoice', 'out_refund', 'in_receipt') else ('allowed_supply_type', 'in', ('out', 'both'))]"/>
|
||||
</group>
|
||||
<group string="Transportation Details" name="transportation_group">
|
||||
<field name="l10n_in_mode" widget="radio" options="{'horizontal': True}"/>
|
||||
<label for="l10n_in_distance"/>
|
||||
<div class="o_row" name="l10n_in_distance">
|
||||
<field name="l10n_in_distance" class="oe_inline"/>
|
||||
<span>km</span>
|
||||
</div>
|
||||
<field name="l10n_in_vehicle_type" widget="radio" options="{'horizontal': True}"
|
||||
attrs="{'invisible': [('l10n_in_mode','!=','1')], 'required': [('l10n_in_mode','=','1')]}"/>
|
||||
<field name="l10n_in_vehicle_no"
|
||||
attrs="{'invisible': [('l10n_in_mode','!=','1')], 'required': [('l10n_in_mode','=','1')]}"/>
|
||||
<field name="l10n_in_transporter_id"
|
||||
domain="[('vat','not in', ['', False]), ('country_id.code','=','IN')]"
|
||||
attrs="{'invisible': [('l10n_in_mode','!=','0')], 'required': [('l10n_in_mode','=','0')]}"/>
|
||||
<field name="l10n_in_transportation_doc_no" string="Transporter Document Number"
|
||||
attrs="{'invisible': [('l10n_in_mode','not in',('2','3','4'))], 'required': [('l10n_in_mode','in',('2','3','4'))]}"/>
|
||||
<field name="l10n_in_transportation_doc_date"
|
||||
attrs="{'invisible': [('l10n_in_mode','not in',('2','3','4'))], 'required': [('l10n_in_mode','in',('2','3','4'))]}"/>
|
||||
</group>
|
||||
<group string="Cancel Reason" attrs="{'invisible': ['|', '|',
|
||||
('country_code', '!=', 'IN'),
|
||||
('state', '!=', 'posted'),
|
||||
('l10n_in_edi_show_cancel', '!=', True)]}">
|
||||
<field name="l10n_in_edi_cancel_reason"/>
|
||||
<field name="l10n_in_edi_cancel_remarks"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="l10n_in_einvoice_report_invoice_document_inherit" inherit_id="account.report_invoice_document">
|
||||
<xpath expr="//div[@id='informations']" position="inside">
|
||||
<t t-set="l10n_in_ewaybill_json" t-value="o._get_l10n_in_edi_ewaybill_response_json()"/>
|
||||
<div id="l10n_in_ewaybill_informations" class="col-auto col-3 mw-100 mb-2" t-if="l10n_in_ewaybill_json" name="ewaybill_number">
|
||||
<strong>E-waybill:</strong>
|
||||
<p t-if="'EwbNo' in l10n_in_ewaybill_json" class="m-0" t-esc="l10n_in_ewaybill_json['EwbNo']"/>
|
||||
<p t-if="'ewayBillNo' in l10n_in_ewaybill_json" class="m-0" t-esc="l10n_in_ewaybill_json['ewayBillNo']"/>
|
||||
Until: <t t-if="'EwbValidTill' in l10n_in_ewaybill_json" class="m-0" t-esc="l10n_in_ewaybill_json['EwbValidTill']"/>
|
||||
<t t-if="'validUpto' in l10n_in_ewaybill_json" class="m-0" t-esc="l10n_in_ewaybill_json['validUpto']"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="res_config_settings_view_form_inherit_l10n_in_edi_ewaybill" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.form.inherit.l10n_in_edi_ewaybill</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div data-key="account" position="inside">
|
||||
<h2 attrs="{'invisible': [('country_code', '!=', 'IN')]}">Indian Electronic WayBill</h2>
|
||||
<div class='row mt16 o_settings_container' name="l10n_in_edi_ewaybill_iap" attrs="{'invisible': [('country_code', '!=', 'IN')]}">
|
||||
<div class="col-12 col-lg-6 o_setting_box" id="gsp_setting">
|
||||
<div class="o_setting_right_pane">
|
||||
<span class="o_form_label">Setup E-Waybill</span>
|
||||
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." aria-label="Values set here are company-specific." groups="base.group_multi_company" role="img"/>
|
||||
<div class="text-muted">
|
||||
Check the <a href="https://www.odoo.com/documentation/16.0/applications/finance/fiscal_localizations/india.html">documentation</a> to get credentials
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="row mt16">
|
||||
<label for="l10n_in_edi_ewaybill_username" class="col-lg-4 o_light_label" string="Username"/>
|
||||
<field name="l10n_in_edi_ewaybill_username"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="l10n_in_edi_ewaybill_password" class="col-lg-4 o_light_label" string="Password"/>
|
||||
<field name="l10n_in_edi_ewaybill_password" password="True"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="l10n_in_edi_production_env" class="col-lg-4 o_light_label" string="Production Environment"/>
|
||||
<field name="l10n_in_edi_production_env"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class='mt8'>
|
||||
<button name="l10n_in_edi_ewaybill_test" icon="fa-arrow-right" type="object" string="Verify Username and Password" class="btn-link"/>
|
||||
</div>
|
||||
<div class='mt8'>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
42
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/pyproject.toml
Normal file
42
odoo-bringout-oca-ocb-l10n_in_edi_ewaybill/pyproject.toml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
[project]
|
||||
name = "odoo-bringout-oca-ocb-l10n_in_edi_ewaybill"
|
||||
version = "16.0.0"
|
||||
description = "Indian - E-waybill - Odoo addon"
|
||||
authors = [
|
||||
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
|
||||
]
|
||||
dependencies = [
|
||||
"odoo-bringout-oca-ocb-l10n_in_edi>=16.0.0",
|
||||
"requests>=2.25.1"
|
||||
]
|
||||
readme = "README.md"
|
||||
requires-python = ">= 3.11"
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"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.12",
|
||||
"Topic :: Office/Business",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://github.com/bringout/0"
|
||||
repository = "https://github.com/bringout/0"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.metadata]
|
||||
allow-direct-references = true
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["l10n_in_edi_ewaybill"]
|
||||
|
||||
[tool.rye]
|
||||
managed = true
|
||||
dev-dependencies = [
|
||||
"pytest>=8.4.1",
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue