blob: 40fa6cd097c439e820a068178683b92a0dcfe6ba [file] [log] [blame]
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe, os, json
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.permissions import add_permission, update_permission_property
from erpnext.regional.india import states
from erpnext.accounts.utils import get_fiscal_year, FiscalYearError
from frappe.utils import today
def setup(company=None, patch=True):
# Company independent fixtures should be called only once at the first company setup
if patch or frappe.db.count("Company", {"country": "India"}) <= 1:
setup_company_independent_fixtures(patch=patch)
if not patch:
make_fixtures(company)
# TODO: for all countries
def setup_company_independent_fixtures(patch=False):
make_custom_fields()
make_property_setters(patch=patch)
add_permissions()
add_custom_roles_for_reports()
frappe.enqueue("erpnext.regional.india.setup.add_hsn_sac_codes", now=frappe.flags.in_test)
create_gratuity_rule()
add_print_formats()
update_accounts_settings_for_taxes()
def add_hsn_sac_codes():
if frappe.flags.in_test and frappe.flags.created_hsn_codes:
return
# HSN codes
with open(os.path.join(os.path.dirname(__file__), "hsn_code_data.json"), "r") as f:
hsn_codes = json.loads(f.read())
create_hsn_codes(hsn_codes, code_field="hsn_code")
# SAC Codes
with open(os.path.join(os.path.dirname(__file__), "sac_code_data.json"), "r") as f:
sac_codes = json.loads(f.read())
create_hsn_codes(sac_codes, code_field="sac_code")
if frappe.flags.in_test:
frappe.flags.created_hsn_codes = True
def create_hsn_codes(data, code_field):
for d in data:
hsn_code = frappe.new_doc("GST HSN Code")
hsn_code.description = d["description"]
hsn_code.hsn_code = d[code_field]
hsn_code.name = d[code_field]
hsn_code.db_insert(ignore_if_duplicate=True)
def add_custom_roles_for_reports():
for report_name in (
"GST Sales Register",
"GST Purchase Register",
"GST Itemised Sales Register",
"GST Itemised Purchase Register",
"Eway Bill",
"E-Invoice Summary",
):
if not frappe.db.get_value("Custom Role", dict(report=report_name)):
frappe.get_doc(
dict(
doctype="Custom Role",
report=report_name,
roles=[dict(role="Accounts User"), dict(role="Accounts Manager")],
)
).insert()
for report_name in ("Professional Tax Deductions", "Provident Fund Deductions"):
if not frappe.db.get_value("Custom Role", dict(report=report_name)):
frappe.get_doc(
dict(
doctype="Custom Role",
report=report_name,
roles=[dict(role="HR User"), dict(role="HR Manager"), dict(role="Employee")],
)
).insert()
for report_name in ("HSN-wise-summary of outward supplies", "GSTR-1", "GSTR-2"):
if not frappe.db.get_value("Custom Role", dict(report=report_name)):
frappe.get_doc(
dict(
doctype="Custom Role",
report=report_name,
roles=[dict(role="Accounts User"), dict(role="Accounts Manager"), dict(role="Auditor")],
)
).insert()
def add_permissions():
for doctype in (
"GST HSN Code",
"GST Settings",
"GSTR 3B Report",
"Lower Deduction Certificate",
"E Invoice Settings",
):
add_permission(doctype, "All", 0)
for role in ("Accounts Manager", "Accounts User", "System Manager"):
add_permission(doctype, role, 0)
update_permission_property(doctype, role, 0, "write", 1)
update_permission_property(doctype, role, 0, "create", 1)
if doctype == "GST HSN Code":
for role in ("Item Manager", "Stock Manager"):
add_permission(doctype, role, 0)
update_permission_property(doctype, role, 0, "write", 1)
update_permission_property(doctype, role, 0, "create", 1)
def add_print_formats():
frappe.reload_doc("regional", "print_format", "gst_tax_invoice")
frappe.reload_doc("selling", "print_format", "gst_pos_invoice")
frappe.reload_doc("accounts", "print_format", "GST E-Invoice")
frappe.db.set_value("Print Format", "GST POS Invoice", "disabled", 0)
frappe.db.set_value("Print Format", "GST Tax Invoice", "disabled", 0)
frappe.db.set_value("Print Format", "GST E-Invoice", "disabled", 0)
def make_property_setters(patch=False):
# GST rules do not allow for an invoice no. bigger than 16 characters
journal_entry_types = frappe.get_meta("Journal Entry").get_options("voucher_type").split("\n") + [
"Reversal Of ITC"
]
sales_invoice_series = ["SINV-.YY.-", "SRET-.YY.-", ""] + frappe.get_meta(
"Sales Invoice"
).get_options("naming_series").split("\n")
purchase_invoice_series = ["PINV-.YY.-", "PRET-.YY.-", ""] + frappe.get_meta(
"Purchase Invoice"
).get_options("naming_series").split("\n")
if not patch:
make_property_setter(
"Sales Invoice", "naming_series", "options", "\n".join(sales_invoice_series), ""
)
make_property_setter(
"Purchase Invoice", "naming_series", "options", "\n".join(purchase_invoice_series), ""
)
make_property_setter(
"Journal Entry", "voucher_type", "options", "\n".join(journal_entry_types), ""
)
def make_custom_fields(update=True):
custom_fields = get_custom_fields()
create_custom_fields(custom_fields, update=update)
def get_custom_fields():
hsn_sac_field = dict(
fieldname="gst_hsn_code",
label="HSN/SAC",
fieldtype="Data",
fetch_from="item_code.gst_hsn_code",
insert_after="description",
allow_on_submit=1,
print_hide=1,
fetch_if_empty=1,
)
nil_rated_exempt = dict(
fieldname="is_nil_exempt",
label="Is Nil Rated or Exempted",
fieldtype="Check",
fetch_from="item_code.is_nil_exempt",
insert_after="gst_hsn_code",
print_hide=1,
)
is_non_gst = dict(
fieldname="is_non_gst",
label="Is Non GST",
fieldtype="Check",
fetch_from="item_code.is_non_gst",
insert_after="is_nil_exempt",
print_hide=1,
)
taxable_value = dict(
fieldname="taxable_value",
label="Taxable Value",
fieldtype="Currency",
insert_after="base_net_amount",
hidden=1,
options="Company:company:default_currency",
print_hide=1,
)
purchase_invoice_gst_category = [
dict(
fieldname="gst_section",
label="GST Details",
fieldtype="Section Break",
insert_after="language",
print_hide=1,
collapsible=1,
),
dict(
fieldname="gst_category",
label="GST Category",
fieldtype="Select",
insert_after="gst_section",
print_hide=1,
options="\nRegistered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nUIN Holders",
fetch_from="supplier.gst_category",
fetch_if_empty=1,
),
dict(
fieldname="export_type",
label="Export Type",
fieldtype="Select",
insert_after="gst_category",
print_hide=1,
depends_on='eval:in_list(["SEZ", "Overseas"], doc.gst_category)',
options="\nWith Payment of Tax\nWithout Payment of Tax",
fetch_from="supplier.export_type",
fetch_if_empty=1,
),
]
sales_invoice_gst_category = [
dict(
fieldname="gst_section",
label="GST Details",
fieldtype="Section Break",
insert_after="language",
print_hide=1,
collapsible=1,
),
dict(
fieldname="gst_category",
label="GST Category",
fieldtype="Select",
insert_after="gst_section",
print_hide=1,
options="\nRegistered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders",
fetch_from="customer.gst_category",
fetch_if_empty=1,
length=25,
),
dict(
fieldname="export_type",
label="Export Type",
fieldtype="Select",
insert_after="gst_category",
print_hide=1,
depends_on='eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)',
options="\nWith Payment of Tax\nWithout Payment of Tax",
fetch_from="customer.export_type",
fetch_if_empty=1,
length=25,
),
]
delivery_note_gst_category = [
dict(
fieldname="gst_category",
label="GST Category",
fieldtype="Select",
insert_after="gst_vehicle_type",
print_hide=1,
options="\nRegistered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders",
fetch_from="customer.gst_category",
fetch_if_empty=1,
),
]
invoice_gst_fields = [
dict(
fieldname="invoice_copy",
label="Invoice Copy",
length=30,
fieldtype="Select",
insert_after="export_type",
print_hide=1,
allow_on_submit=1,
options="Original for Recipient\nDuplicate for Transporter\nDuplicate for Supplier\nTriplicate for Supplier",
),
dict(
fieldname="reverse_charge",
label="Reverse Charge",
length=2,
fieldtype="Select",
insert_after="invoice_copy",
print_hide=1,
options="Y\nN",
default="N",
),
dict(
fieldname="ecommerce_gstin",
label="E-commerce GSTIN",
length=15,
fieldtype="Data",
insert_after="export_type",
print_hide=1,
),
dict(fieldname="gst_col_break", fieldtype="Column Break", insert_after="ecommerce_gstin"),
dict(
fieldname="reason_for_issuing_document",
label="Reason For Issuing document",
fieldtype="Select",
insert_after="gst_col_break",
print_hide=1,
depends_on="eval:doc.is_return==1",
length=45,
options="\n01-Sales Return\n02-Post Sale Discount\n03-Deficiency in services\n04-Correction in Invoice\n05-Change in POS\n06-Finalization of Provisional assessment\n07-Others",
),
]
purchase_invoice_gst_fields = [
dict(
fieldname="supplier_gstin",
label="Supplier GSTIN",
fieldtype="Data",
insert_after="supplier_address",
fetch_from="supplier_address.gstin",
print_hide=1,
read_only=1,
),
dict(
fieldname="company_gstin",
label="Company GSTIN",
fieldtype="Data",
insert_after="shipping_address_display",
fetch_from="shipping_address.gstin",
print_hide=1,
read_only=1,
),
dict(
fieldname="place_of_supply",
label="Place of Supply",
fieldtype="Data",
insert_after="shipping_address",
print_hide=1,
read_only=1,
),
]
purchase_invoice_itc_fields = [
dict(
fieldname="eligibility_for_itc",
label="Eligibility For ITC",
fieldtype="Select",
insert_after="reason_for_issuing_document",
print_hide=1,
options="Input Service Distributor\nImport Of Service\nImport Of Capital Goods\nITC on Reverse Charge\nIneligible As Per Section 17(5)\nIneligible Others\nAll Other ITC",
default="All Other ITC",
),
dict(
fieldname="itc_integrated_tax",
label="Availed ITC Integrated Tax",
fieldtype="Currency",
insert_after="eligibility_for_itc",
options="Company:company:default_currency",
print_hide=1,
),
dict(
fieldname="itc_central_tax",
label="Availed ITC Central Tax",
fieldtype="Currency",
insert_after="itc_integrated_tax",
options="Company:company:default_currency",
print_hide=1,
),
dict(
fieldname="itc_state_tax",
label="Availed ITC State/UT Tax",
fieldtype="Currency",
insert_after="itc_central_tax",
options="Company:company:default_currency",
print_hide=1,
),
dict(
fieldname="itc_cess_amount",
label="Availed ITC Cess",
fieldtype="Currency",
insert_after="itc_state_tax",
options="Company:company:default_currency",
print_hide=1,
),
]
sales_invoice_gst_fields = [
dict(
fieldname="billing_address_gstin",
label="Billing Address GSTIN",
fieldtype="Data",
insert_after="customer_address",
read_only=1,
fetch_from="customer_address.gstin",
print_hide=1,
length=15,
),
dict(
fieldname="customer_gstin",
label="Customer GSTIN",
fieldtype="Data",
insert_after="shipping_address_name",
fetch_from="shipping_address_name.gstin",
print_hide=1,
length=15,
),
dict(
fieldname="place_of_supply",
label="Place of Supply",
fieldtype="Data",
insert_after="customer_gstin",
print_hide=1,
read_only=1,
length=50,
),
dict(
fieldname="company_gstin",
label="Company GSTIN",
fieldtype="Data",
insert_after="company_address",
fetch_from="company_address.gstin",
print_hide=1,
read_only=1,
length=15,
),
]
sales_invoice_shipping_fields = [
dict(
fieldname="port_code",
label="Port Code",
fieldtype="Data",
insert_after="reason_for_issuing_document",
print_hide=1,
depends_on="eval:doc.gst_category=='Overseas' ",
length=15,
),
dict(
fieldname="shipping_bill_number",
label=" Shipping Bill Number",
fieldtype="Data",
insert_after="port_code",
print_hide=1,
depends_on="eval:doc.gst_category=='Overseas' ",
length=50,
),
dict(
fieldname="shipping_bill_date",
label="Shipping Bill Date",
fieldtype="Date",
insert_after="shipping_bill_number",
print_hide=1,
depends_on="eval:doc.gst_category=='Overseas' ",
),
]
journal_entry_fields = [
dict(
fieldname="reversal_type",
label="Reversal Type",
fieldtype="Select",
insert_after="voucher_type",
print_hide=1,
options="As per rules 42 & 43 of CGST Rules\nOthers",
depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
),
dict(
fieldname="company_address",
label="Company Address",
fieldtype="Link",
options="Address",
insert_after="reversal_type",
print_hide=1,
depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
),
dict(
fieldname="company_gstin",
label="Company GSTIN",
fieldtype="Data",
read_only=1,
insert_after="company_address",
print_hide=1,
fetch_from="company_address.gstin",
depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
),
]
inter_state_gst_field = [
dict(
fieldname="is_inter_state",
label="Is Inter State",
fieldtype="Check",
insert_after="disabled",
print_hide=1,
),
dict(
fieldname="is_reverse_charge",
label="Is Reverse Charge",
fieldtype="Check",
insert_after="is_inter_state",
print_hide=1,
),
dict(
fieldname="tax_category_column_break",
fieldtype="Column Break",
insert_after="is_reverse_charge",
),
dict(
fieldname="gst_state",
label="Source State",
fieldtype="Select",
options="\n".join(states),
insert_after="company",
),
]
ewaybill_fields = [
{
"fieldname": "distance",
"label": "Distance (in km)",
"fieldtype": "Float",
"insert_after": "vehicle_no",
"print_hide": 1,
},
{
"fieldname": "gst_transporter_id",
"label": "GST Transporter ID",
"fieldtype": "Data",
"insert_after": "transporter",
"fetch_from": "transporter.gst_transporter_id",
"print_hide": 1,
"translatable": 0,
},
{
"fieldname": "mode_of_transport",
"label": "Mode of Transport",
"fieldtype": "Select",
"options": "\nRoad\nAir\nRail\nShip",
"default": "Road",
"insert_after": "transporter_name",
"print_hide": 1,
"translatable": 0,
},
{
"fieldname": "gst_vehicle_type",
"label": "GST Vehicle Type",
"fieldtype": "Select",
"options": "Regular\nOver Dimensional Cargo (ODC)",
"depends_on": 'eval:(doc.mode_of_transport === "Road")',
"default": "Regular",
"insert_after": "lr_date",
"print_hide": 1,
"translatable": 0,
},
{
"fieldname": "ewaybill",
"label": "E-Way Bill No.",
"fieldtype": "Data",
"depends_on": "eval:(doc.docstatus === 1)",
"allow_on_submit": 1,
"insert_after": "customer_name_in_arabic",
"translatable": 0,
},
]
si_ewaybill_fields = [
{
"fieldname": "transporter_info",
"label": "Transporter Info",
"fieldtype": "Section Break",
"insert_after": "terms",
"collapsible": 1,
"collapsible_depends_on": "transporter",
"print_hide": 1,
},
{
"fieldname": "transporter",
"label": "Transporter",
"fieldtype": "Link",
"insert_after": "transporter_info",
"options": "Supplier",
"print_hide": 1,
},
{
"fieldname": "gst_transporter_id",
"label": "GST Transporter ID",
"fieldtype": "Data",
"insert_after": "transporter",
"fetch_from": "transporter.gst_transporter_id",
"print_hide": 1,
"translatable": 0,
"length": 20,
},
{
"fieldname": "driver",
"label": "Driver",
"fieldtype": "Link",
"insert_after": "gst_transporter_id",
"options": "Driver",
"print_hide": 1,
},
{
"fieldname": "lr_no",
"label": "Transport Receipt No",
"fieldtype": "Data",
"insert_after": "driver",
"print_hide": 1,
"translatable": 0,
"length": 30,
},
{
"fieldname": "vehicle_no",
"label": "Vehicle No",
"fieldtype": "Data",
"insert_after": "lr_no",
"print_hide": 1,
"translatable": 0,
"length": 10,
},
{
"fieldname": "distance",
"label": "Distance (in km)",
"fieldtype": "Float",
"insert_after": "vehicle_no",
"print_hide": 1,
},
{"fieldname": "transporter_col_break", "fieldtype": "Column Break", "insert_after": "distance"},
{
"fieldname": "transporter_name",
"label": "Transporter Name",
"fieldtype": "Small Text",
"insert_after": "transporter_col_break",
"fetch_from": "transporter.name",
"read_only": 1,
"print_hide": 1,
"translatable": 0,
},
{
"fieldname": "mode_of_transport",
"label": "Mode of Transport",
"fieldtype": "Select",
"options": "\nRoad\nAir\nRail\nShip",
"insert_after": "transporter_name",
"print_hide": 1,
"translatable": 0,
"length": 5,
},
{
"fieldname": "driver_name",
"label": "Driver Name",
"fieldtype": "Small Text",
"insert_after": "mode_of_transport",
"fetch_from": "driver.full_name",
"print_hide": 1,
"translatable": 0,
},
{
"fieldname": "lr_date",
"label": "Transport Receipt Date",
"fieldtype": "Date",
"insert_after": "driver_name",
"default": "Today",
"print_hide": 1,
},
{
"fieldname": "gst_vehicle_type",
"label": "GST Vehicle Type",
"fieldtype": "Select",
"options": "Regular\nOver Dimensional Cargo (ODC)",
"depends_on": 'eval:(doc.mode_of_transport === "Road")',
"default": "Regular",
"insert_after": "lr_date",
"print_hide": 1,
"translatable": 0,
"length": 30,
},
{
"fieldname": "ewaybill",
"label": "E-Way Bill No.",
"fieldtype": "Data",
"depends_on": "eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)",
"allow_on_submit": 1,
"insert_after": "tax_id",
"translatable": 0,
"length": 20,
},
]
payment_entry_fields = [
dict(
fieldname="gst_section",
label="GST Details",
fieldtype="Section Break",
insert_after="deductions",
print_hide=1,
collapsible=1,
),
dict(
fieldname="company_address",
label="Company Address",
fieldtype="Link",
insert_after="gst_section",
print_hide=1,
options="Address",
),
dict(
fieldname="company_gstin",
label="Company GSTIN",
fieldtype="Data",
insert_after="company_address",
fetch_from="company_address.gstin",
print_hide=1,
read_only=1,
),
dict(
fieldname="place_of_supply",
label="Place of Supply",
fieldtype="Data",
insert_after="company_gstin",
print_hide=1,
read_only=1,
),
dict(fieldname="gst_column_break", fieldtype="Column Break", insert_after="place_of_supply"),
dict(
fieldname="customer_address",
label="Customer Address",
fieldtype="Link",
insert_after="gst_column_break",
print_hide=1,
options="Address",
depends_on='eval:doc.party_type == "Customer"',
),
dict(
fieldname="customer_gstin",
label="Customer GSTIN",
fieldtype="Data",
insert_after="customer_address",
fetch_from="customer_address.gstin",
print_hide=1,
read_only=1,
),
]
si_einvoice_fields = [
dict(
fieldname="irn",
label="IRN",
fieldtype="Data",
read_only=1,
insert_after="customer",
no_copy=1,
print_hide=1,
depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0',
),
dict(
fieldname="irn_cancelled",
label="IRN Cancelled",
fieldtype="Check",
no_copy=1,
print_hide=1,
depends_on="eval: doc.irn",
allow_on_submit=1,
insert_after="customer",
),
dict(
fieldname="eway_bill_validity",
label="E-Way Bill Validity",
fieldtype="Data",
no_copy=1,
print_hide=1,
depends_on="ewaybill",
read_only=1,
allow_on_submit=1,
insert_after="ewaybill",
),
dict(
fieldname="eway_bill_cancelled",
label="E-Way Bill Cancelled",
fieldtype="Check",
no_copy=1,
print_hide=1,
depends_on="eval:(doc.eway_bill_cancelled === 1)",
read_only=1,
allow_on_submit=1,
insert_after="customer",
),
dict(
fieldname="einvoice_section",
label="E-Invoice Fields",
fieldtype="Section Break",
insert_after="gst_vehicle_type",
print_hide=1,
hidden=1,
),
dict(
fieldname="ack_no",
label="Ack. No.",
fieldtype="Data",
read_only=1,
hidden=1,
insert_after="einvoice_section",
no_copy=1,
print_hide=1,
),
dict(
fieldname="ack_date",
label="Ack. Date",
fieldtype="Data",
read_only=1,
hidden=1,
insert_after="ack_no",
no_copy=1,
print_hide=1,
),
dict(
fieldname="irn_cancel_date",
label="Cancel Date",
fieldtype="Data",
read_only=1,
hidden=1,
insert_after="ack_date",
no_copy=1,
print_hide=1,
),
dict(
fieldname="signed_einvoice",
label="Signed E-Invoice",
fieldtype="Code",
options="JSON",
hidden=1,
insert_after="irn_cancel_date",
no_copy=1,
print_hide=1,
read_only=1,
),
dict(
fieldname="signed_qr_code",
label="Signed QRCode",
fieldtype="Code",
options="JSON",
hidden=1,
insert_after="signed_einvoice",
no_copy=1,
print_hide=1,
read_only=1,
),
dict(
fieldname="qrcode_image",
label="QRCode",
fieldtype="Attach Image",
hidden=1,
insert_after="signed_qr_code",
no_copy=1,
print_hide=1,
read_only=1,
),
dict(
fieldname="einvoice_status",
label="E-Invoice Status",
fieldtype="Select",
insert_after="qrcode_image",
options="\nPending\nGenerated\nCancelled\nFailed",
default=None,
hidden=1,
no_copy=1,
print_hide=1,
read_only=1,
),
dict(
fieldname="failure_description",
label="E-Invoice Failure Description",
fieldtype="Code",
options="JSON",
hidden=1,
insert_after="einvoice_status",
no_copy=1,
print_hide=1,
read_only=1,
),
]
custom_fields = {
"Address": [
dict(fieldname="gstin", label="Party GSTIN", fieldtype="Data", insert_after="fax"),
dict(
fieldname="gst_state",
label="GST State",
fieldtype="Select",
options="\n".join(states),
insert_after="gstin",
),
dict(
fieldname="gst_state_number",
label="GST State Number",
fieldtype="Data",
insert_after="gst_state",
read_only=1,
),
],
"Purchase Invoice": purchase_invoice_gst_category
+ invoice_gst_fields
+ purchase_invoice_itc_fields
+ purchase_invoice_gst_fields,
"Purchase Order": purchase_invoice_gst_fields,
"Purchase Receipt": purchase_invoice_gst_fields,
"Sales Invoice": sales_invoice_gst_category
+ invoice_gst_fields
+ sales_invoice_shipping_fields
+ sales_invoice_gst_fields
+ si_ewaybill_fields
+ si_einvoice_fields,
"POS Invoice": sales_invoice_gst_fields,
"Delivery Note": sales_invoice_gst_fields
+ ewaybill_fields
+ sales_invoice_shipping_fields
+ delivery_note_gst_category,
"Payment Entry": payment_entry_fields,
"Journal Entry": journal_entry_fields,
"Sales Order": sales_invoice_gst_fields,
"Tax Category": inter_state_gst_field,
"Item": [
dict(
fieldname="gst_hsn_code",
label="HSN/SAC",
fieldtype="Link",
options="GST HSN Code",
insert_after="item_group",
),
dict(
fieldname="is_nil_exempt",
label="Is Nil Rated or Exempted",
fieldtype="Check",
insert_after="gst_hsn_code",
),
dict(
fieldname="is_non_gst", label="Is Non GST ", fieldtype="Check", insert_after="is_nil_exempt"
),
],
"Quotation Item": [hsn_sac_field, nil_rated_exempt, is_non_gst],
"Supplier Quotation Item": [hsn_sac_field, nil_rated_exempt, is_non_gst],
"Sales Order Item": [hsn_sac_field, nil_rated_exempt, is_non_gst],
"Delivery Note Item": [hsn_sac_field, nil_rated_exempt, is_non_gst],
"Sales Invoice Item": [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
"POS Invoice Item": [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
"Purchase Order Item": [hsn_sac_field, nil_rated_exempt, is_non_gst],
"Purchase Receipt Item": [hsn_sac_field, nil_rated_exempt, is_non_gst],
"Purchase Invoice Item": [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
"Material Request Item": [hsn_sac_field, nil_rated_exempt, is_non_gst],
"Salary Component": [
dict(
fieldname="component_type",
label="Component Type",
fieldtype="Select",
insert_after="description",
options="\nProvident Fund\nAdditional Provident Fund\nProvident Fund Loan\nProfessional Tax",
depends_on='eval:doc.type == "Deduction"',
)
],
"Employee": [
dict(
fieldname="ifsc_code",
label="IFSC Code",
fieldtype="Data",
insert_after="bank_ac_no",
print_hide=1,
depends_on='eval:doc.salary_mode == "Bank"',
),
dict(
fieldname="pan_number",
label="PAN Number",
fieldtype="Data",
insert_after="payroll_cost_center",
print_hide=1,
),
dict(
fieldname="micr_code",
label="MICR Code",
fieldtype="Data",
insert_after="ifsc_code",
print_hide=1,
depends_on='eval:doc.salary_mode == "Bank"',
),
dict(
fieldname="provident_fund_account",
label="Provident Fund Account",
fieldtype="Data",
insert_after="pan_number",
),
],
"Company": [
dict(
fieldname="hra_section",
label="HRA Settings",
fieldtype="Section Break",
insert_after="asset_received_but_not_billed",
collapsible=1,
),
dict(
fieldname="basic_component",
label="Basic Component",
fieldtype="Link",
options="Salary Component",
insert_after="hra_section",
),
dict(
fieldname="hra_component",
label="HRA Component",
fieldtype="Link",
options="Salary Component",
insert_after="basic_component",
),
dict(fieldname="hra_column_break", fieldtype="Column Break", insert_after="hra_component"),
dict(
fieldname="arrear_component",
label="Arrear Component",
fieldtype="Link",
options="Salary Component",
insert_after="hra_column_break",
),
],
"Employee Tax Exemption Declaration": [
dict(
fieldname="hra_section",
label="HRA Exemption",
fieldtype="Section Break",
insert_after="declarations",
),
dict(
fieldname="monthly_house_rent",
label="Monthly House Rent",
fieldtype="Currency",
insert_after="hra_section",
),
dict(
fieldname="rented_in_metro_city",
label="Rented in Metro City",
fieldtype="Check",
insert_after="monthly_house_rent",
depends_on="monthly_house_rent",
),
dict(
fieldname="salary_structure_hra",
label="HRA as per Salary Structure",
fieldtype="Currency",
insert_after="rented_in_metro_city",
read_only=1,
depends_on="monthly_house_rent",
),
dict(
fieldname="hra_column_break",
fieldtype="Column Break",
insert_after="salary_structure_hra",
depends_on="monthly_house_rent",
),
dict(
fieldname="annual_hra_exemption",
label="Annual HRA Exemption",
fieldtype="Currency",
insert_after="hra_column_break",
read_only=1,
depends_on="monthly_house_rent",
),
dict(
fieldname="monthly_hra_exemption",
label="Monthly HRA Exemption",
fieldtype="Currency",
insert_after="annual_hra_exemption",
read_only=1,
depends_on="monthly_house_rent",
),
],
"Employee Tax Exemption Proof Submission": [
dict(
fieldname="hra_section",
label="HRA Exemption",
fieldtype="Section Break",
insert_after="tax_exemption_proofs",
),
dict(
fieldname="house_rent_payment_amount",
label="House Rent Payment Amount",
fieldtype="Currency",
insert_after="hra_section",
),
dict(
fieldname="rented_in_metro_city",
label="Rented in Metro City",
fieldtype="Check",
insert_after="house_rent_payment_amount",
depends_on="house_rent_payment_amount",
),
dict(
fieldname="rented_from_date",
label="Rented From Date",
fieldtype="Date",
insert_after="rented_in_metro_city",
depends_on="house_rent_payment_amount",
),
dict(
fieldname="rented_to_date",
label="Rented To Date",
fieldtype="Date",
insert_after="rented_from_date",
depends_on="house_rent_payment_amount",
),
dict(
fieldname="hra_column_break",
fieldtype="Column Break",
insert_after="rented_to_date",
depends_on="house_rent_payment_amount",
),
dict(
fieldname="monthly_house_rent",
label="Monthly House Rent",
fieldtype="Currency",
insert_after="hra_column_break",
read_only=1,
depends_on="house_rent_payment_amount",
),
dict(
fieldname="monthly_hra_exemption",
label="Monthly Eligible Amount",
fieldtype="Currency",
insert_after="monthly_house_rent",
read_only=1,
depends_on="house_rent_payment_amount",
),
dict(
fieldname="total_eligible_hra_exemption",
label="Total Eligible HRA Exemption",
fieldtype="Currency",
insert_after="monthly_hra_exemption",
read_only=1,
depends_on="house_rent_payment_amount",
),
],
"Supplier": [
{"fieldname": "pan", "label": "PAN", "fieldtype": "Data", "insert_after": "supplier_type"},
{
"fieldname": "gst_transporter_id",
"label": "GST Transporter ID",
"fieldtype": "Data",
"insert_after": "pan",
"depends_on": "eval:doc.is_transporter",
},
{
"fieldname": "gst_category",
"label": "GST Category",
"fieldtype": "Select",
"insert_after": "gst_transporter_id",
"options": "Registered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nUIN Holders",
"default": "Unregistered",
},
{
"fieldname": "export_type",
"label": "Export Type",
"fieldtype": "Select",
"insert_after": "gst_category",
"depends_on": 'eval:in_list(["SEZ", "Overseas"], doc.gst_category)',
"options": "\nWith Payment of Tax\nWithout Payment of Tax",
"mandatory_depends_on": 'eval:in_list(["SEZ", "Overseas"], doc.gst_category)',
},
],
"Customer": [
{"fieldname": "pan", "label": "PAN", "fieldtype": "Data", "insert_after": "customer_type"},
{
"fieldname": "gst_category",
"label": "GST Category",
"fieldtype": "Select",
"insert_after": "pan",
"options": "Registered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders",
"default": "Unregistered",
},
{
"fieldname": "export_type",
"label": "Export Type",
"fieldtype": "Select",
"insert_after": "gst_category",
"depends_on": 'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)',
"options": "\nWith Payment of Tax\nWithout Payment of Tax",
"mandatory_depends_on": 'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)',
},
],
"Finance Book": [
{
"fieldname": "for_income_tax",
"label": "For Income Tax",
"fieldtype": "Check",
"insert_after": "finance_book_name",
"description": "If the asset is put to use for less than 180 days, the first Depreciation Rate will be reduced by 50%.",
}
],
}
return custom_fields
def make_fixtures(company=None):
docs = []
company = company or frappe.db.get_value("Global Defaults", None, "default_company")
set_salary_components(docs)
set_tds_account(docs, company)
for d in docs:
try:
doc = frappe.get_doc(d)
doc.flags.ignore_permissions = True
doc.insert()
except frappe.NameError:
frappe.clear_messages()
except frappe.DuplicateEntryError:
frappe.clear_messages()
# create records for Tax Withholding Category
set_tax_withholding_category(company)
def update_regional_tax_settings(country, company):
# Will only add default GST accounts if present
input_account_names = ["Input Tax CGST", "Input Tax SGST", "Input Tax IGST"]
output_account_names = ["Output Tax CGST", "Output Tax SGST", "Output Tax IGST"]
rcm_accounts = ["Input Tax CGST RCM", "Input Tax SGST RCM", "Input Tax IGST RCM"]
gst_settings = frappe.get_single("GST Settings")
existing_account_list = []
for account in gst_settings.get("gst_accounts"):
for key in ["cgst_account", "sgst_account", "igst_account"]:
existing_account_list.append(account.get(key))
gst_accounts = frappe._dict(
frappe.get_all(
"Account",
{
"company": company,
"account_name": ("in", input_account_names + output_account_names + rcm_accounts),
},
["account_name", "name"],
as_list=1,
)
)
add_accounts_in_gst_settings(
company, input_account_names, gst_accounts, existing_account_list, gst_settings
)
add_accounts_in_gst_settings(
company, output_account_names, gst_accounts, existing_account_list, gst_settings
)
add_accounts_in_gst_settings(
company, rcm_accounts, gst_accounts, existing_account_list, gst_settings, is_reverse_charge=1
)
gst_settings.save()
def add_accounts_in_gst_settings(
company, account_names, gst_accounts, existing_account_list, gst_settings, is_reverse_charge=0
):
accounts_not_added = 1
for account in account_names:
# Default Account Added does not exists
if not gst_accounts.get(account):
accounts_not_added = 0
# Check if already added in GST Settings
if gst_accounts.get(account) in existing_account_list:
accounts_not_added = 0
if accounts_not_added:
gst_settings.append(
"gst_accounts",
{
"company": company,
"cgst_account": gst_accounts.get(account_names[0]),
"sgst_account": gst_accounts.get(account_names[1]),
"igst_account": gst_accounts.get(account_names[2]),
"is_reverse_charge_account": is_reverse_charge,
},
)
def set_salary_components(docs):
docs.extend(
[
{
"doctype": "Salary Component",
"salary_component": "Professional Tax",
"description": "Professional Tax",
"type": "Deduction",
"exempted_from_income_tax": 1,
},
{
"doctype": "Salary Component",
"salary_component": "Provident Fund",
"description": "Provident fund",
"type": "Deduction",
"is_tax_applicable": 1,
},
{
"doctype": "Salary Component",
"salary_component": "House Rent Allowance",
"description": "House Rent Allowance",
"type": "Earning",
"is_tax_applicable": 1,
},
{
"doctype": "Salary Component",
"salary_component": "Basic",
"description": "Basic",
"type": "Earning",
"is_tax_applicable": 1,
},
{
"doctype": "Salary Component",
"salary_component": "Arrear",
"description": "Arrear",
"type": "Earning",
"is_tax_applicable": 1,
},
{
"doctype": "Salary Component",
"salary_component": "Leave Encashment",
"description": "Leave Encashment",
"type": "Earning",
"is_tax_applicable": 1,
},
]
)
def set_tax_withholding_category(company):
accounts = []
fiscal_year_details = None
abbr = frappe.get_value("Company", company, "abbr")
tds_account = frappe.get_value("Account", "TDS Payable - {0}".format(abbr), "name")
if company and tds_account:
accounts = [dict(company=company, account=tds_account)]
try:
fiscal_year_details = get_fiscal_year(today(), verbose=0)
except FiscalYearError:
pass
docs = get_tds_details(accounts, fiscal_year_details)
for d in docs:
if not frappe.db.exists("Tax Withholding Category", d.get("name")):
doc = frappe.get_doc(d)
doc.flags.ignore_validate = True
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
doc.insert()
else:
doc = frappe.get_doc("Tax Withholding Category", d.get("name"), for_update=True)
if accounts:
doc.append("accounts", accounts[0])
if fiscal_year_details:
# if fiscal year don't match with any of the already entered data, append rate row
fy_exist = [
k
for k in doc.get("rates")
if k.get("from_date") <= fiscal_year_details[1] and k.get("to_date") >= fiscal_year_details[2]
]
if not fy_exist:
doc.append("rates", d.get("rates")[0])
doc.flags.ignore_permissions = True
doc.flags.ignore_validate = True
doc.flags.ignore_mandatory = True
doc.flags.ignore_links = True
doc.save()
def set_tds_account(docs, company):
parent_account = frappe.db.get_value(
"Account", filters={"account_name": "Duties and Taxes", "company": company}
)
if parent_account:
docs.extend(
[
{
"doctype": "Account",
"account_name": "TDS Payable",
"account_type": "Tax",
"parent_account": parent_account,
"company": company,
}
]
)
def get_tds_details(accounts, fiscal_year_details):
# bootstrap default tax withholding sections
return [
dict(
name="TDS - 194C - Company",
category_name="Payment to Contractors (Single / Aggregate)",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 2,
"single_threshold": 30000,
"cumulative_threshold": 100000,
}
],
),
dict(
name="TDS - 194C - Individual",
category_name="Payment to Contractors (Single / Aggregate)",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 1,
"single_threshold": 30000,
"cumulative_threshold": 100000,
}
],
),
dict(
name="TDS - 194C - No PAN / Invalid PAN",
category_name="Payment to Contractors (Single / Aggregate)",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 20,
"single_threshold": 30000,
"cumulative_threshold": 100000,
}
],
),
dict(
name="TDS - 194D - Company",
category_name="Insurance Commission",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 5,
"single_threshold": 15000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194D - Company Assessee",
category_name="Insurance Commission",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 10,
"single_threshold": 15000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194D - Individual",
category_name="Insurance Commission",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 5,
"single_threshold": 15000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194D - No PAN / Invalid PAN",
category_name="Insurance Commission",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 20,
"single_threshold": 15000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194DA - Company",
category_name="Non-exempt payments made under a life insurance policy",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 1,
"single_threshold": 100000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194DA - Individual",
category_name="Non-exempt payments made under a life insurance policy",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 1,
"single_threshold": 100000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194DA - No PAN / Invalid PAN",
category_name="Non-exempt payments made under a life insurance policy",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 20,
"single_threshold": 100000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194H - Company",
category_name="Commission / Brokerage",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 5,
"single_threshold": 15000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194H - Individual",
category_name="Commission / Brokerage",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 5,
"single_threshold": 15000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194H - No PAN / Invalid PAN",
category_name="Commission / Brokerage",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 20,
"single_threshold": 15000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194I - Rent - Company",
category_name="Rent",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 10,
"single_threshold": 180000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194I - Rent - Individual",
category_name="Rent",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 10,
"single_threshold": 180000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194I - Rent - No PAN / Invalid PAN",
category_name="Rent",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 20,
"single_threshold": 180000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194I - Rent/Machinery - Company",
category_name="Rent-Plant / Machinery",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 2,
"single_threshold": 180000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194I - Rent/Machinery - Individual",
category_name="Rent-Plant / Machinery",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 2,
"single_threshold": 180000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194I - Rent/Machinery - No PAN / Invalid PAN",
category_name="Rent-Plant / Machinery",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 20,
"single_threshold": 180000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194J - Professional Fees - Company",
category_name="Professional Fees",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 10,
"single_threshold": 30000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194J - Professional Fees - Individual",
category_name="Professional Fees",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 10,
"single_threshold": 30000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194J - Professional Fees - No PAN / Invalid PAN",
category_name="Professional Fees",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 20,
"single_threshold": 30000,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194J - Director Fees - Company",
category_name="Director Fees",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 10,
"single_threshold": 0,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194J - Director Fees - Individual",
category_name="Director Fees",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 10,
"single_threshold": 0,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194J - Director Fees - No PAN / Invalid PAN",
category_name="Director Fees",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 20,
"single_threshold": 0,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194 - Dividends - Company",
category_name="Dividends",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 10,
"single_threshold": 2500,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194 - Dividends - Individual",
category_name="Dividends",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 10,
"single_threshold": 2500,
"cumulative_threshold": 0,
}
],
),
dict(
name="TDS - 194 - Dividends - No PAN / Invalid PAN",
category_name="Dividends",
doctype="Tax Withholding Category",
accounts=accounts,
rates=[
{
"from_date": fiscal_year_details[1],
"to_date": fiscal_year_details[2],
"tax_withholding_rate": 20,
"single_threshold": 2500,
"cumulative_threshold": 0,
}
],
),
]
def create_gratuity_rule():
# Standard Indain Gratuity Rule
if not frappe.db.exists("Gratuity Rule", "Indian Standard Gratuity Rule"):
rule = frappe.new_doc("Gratuity Rule")
rule.name = "Indian Standard Gratuity Rule"
rule.calculate_gratuity_amount_based_on = "Current Slab"
rule.work_experience_calculation_method = "Round Off Work Experience"
rule.minimum_year_for_gratuity = 5
fraction = 15 / 26
rule.append(
"gratuity_rule_slabs",
{"from_year": 0, "to_year": 0, "fraction_of_applicable_earnings": fraction},
)
rule.flags.ignore_mandatory = True
rule.save()
def update_accounts_settings_for_taxes():
if frappe.db.count("Company") == 1:
frappe.db.set_value("Accounts Settings", None, "add_taxes_from_item_tax_template", 0)