Merge pull request #23447 from hasnain2808/UAE-VAT-Format

feat: UAE VAT 201 Report
This commit is contained in:
Deepesh Garg 2020-11-18 15:15:00 +05:30 committed by GitHub
commit f0187f6db2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1052 additions and 12 deletions

View File

@ -79,6 +79,11 @@
"hidden": 0,
"label": "Profitability",
"links": "[\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Gross Profit\",\n \"name\": \"Gross Profit\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profitability Analysis\",\n \"name\": \"Profitability Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Value-Added Tax (VAT UAE)",
"links": "[\n {\n \"country\": \"United Arab Emirates\",\n \"label\": \"UAE VAT Settings\",\n \"name\": \"UAE VAT Settings\",\n \"type\": \"doctype\"\n },\n {\n \"country\": \"United Arab Emirates\",\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n }\n\n]"
}
],
"category": "Modules",

View File

@ -998,7 +998,7 @@ def make_purchase_invoice(**args):
'expense_account': args.expense_account or '_Test Account Cost for Goods Sold - _TC',
"conversion_factor": 1.0,
"serial_no": args.serial_no,
"stock_uom": "_Test UOM",
"stock_uom": args.uom or "_Test UOM",
"cost_center": args.cost_center or "_Test Cost Center - _TC",
"project": args.project,
"rejected_warehouse": args.rejected_warehouse or "",
@ -1040,7 +1040,8 @@ def make_purchase_invoice_against_cost_center(**args):
pi.is_return = args.is_return
pi.credit_to = args.return_against or "Creditors - _TC"
pi.is_subcontracted = args.is_subcontracted or "No"
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
if args.supplier_warehouse:
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
pi.append("items", {
"item_code": args.item or args.item_code or "_Test Item",

View File

@ -250,7 +250,11 @@ doc_events = {
"on_trash": "erpnext.regional.check_deletion_permission"
},
"Purchase Invoice": {
"validate": "erpnext.regional.india.utils.update_grand_total_for_rcm"
"validate": [
"erpnext.regional.india.utils.update_grand_total_for_rcm",
"erpnext.regional.united_arab_emirates.utils.update_grand_total_for_rcm",
"erpnext.regional.united_arab_emirates.utils.validate_returns"
]
},
"Payment Entry": {
"on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status", "erpnext.accounts.doctype.dunning.dunning.resolve_dunning"],
@ -391,7 +395,8 @@ regional_overrides = {
'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries'
},
'United Arab Emirates': {
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data'
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',
'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.united_arab_emirates.utils.make_regional_gl_entries',
},
'Saudi Arabia': {
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data'

View File

@ -0,0 +1,35 @@
{
"actions": [],
"autoname": "account",
"creation": "2020-09-28 11:30:45.472053",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"account"
],
"fields": [
{
"allow_in_quick_entry": 1,
"fieldname": "account",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "Account",
"options": "Account"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-09-28 12:02:56.444007",
"modified_by": "Administrator",
"module": "Regional",
"name": "UAE VAT Account",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class UAEVATAccount(Document):
pass

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestUAEVATSettings(unittest.TestCase):
pass

View File

@ -0,0 +1,8 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('UAE VAT Settings', {
// refresh: function(frm) {
// }
});

View File

@ -0,0 +1,55 @@
{
"actions": [],
"autoname": "field:company",
"creation": "2020-09-25 12:48:51.463265",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"uae_vat_accounts"
],
"fields": [
{
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Company",
"options": "Company",
"reqd": 1,
"unique": 1
},
{
"fieldname": "uae_vat_accounts",
"fieldtype": "Table",
"label": "UAE VAT Accounts",
"options": "UAE VAT Account",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2020-09-30 20:08:18.764798",
"modified_by": "Administrator",
"module": "Regional",
"name": "UAE VAT Settings",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class UAEVATSettings(Document):
pass

View File

@ -0,0 +1,239 @@
# coding=utf-8
from __future__ import unicode_literals
import erpnext
import frappe
from unittest import TestCase
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse_account
from erpnext.regional.report.uae_vat_201.uae_vat_201 import (
get_total_emiratewise,
get_tourist_tax_return_total,
get_tourist_tax_return_tax,
get_zero_rated_total,
get_exempt_total,
get_standard_rated_expenses_total,
get_standard_rated_expenses_tax,
)
test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
class TestUaeVat201(TestCase):
def setUp(self):
frappe.set_user("Administrator")
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company UAE VAT'")
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company UAE VAT'")
make_company("_Test Company UAE VAT", "_TCUV")
set_vat_accounts()
make_customer()
make_supplier()
create_warehouse("_Test UAE VAT Supplier Warehouse", company="_Test Company UAE VAT")
make_item("_Test UAE VAT Item", properties = {"is_zero_rated": 0, "is_exempt": 0})
make_item("_Test UAE VAT Zero Rated Item", properties = {"is_zero_rated": 1, "is_exempt": 0})
make_item("_Test UAE VAT Exempt Item", properties = {"is_zero_rated": 0, "is_exempt": 1})
make_sales_invoices()
create_purchase_invoices()
def test_uae_vat_201_report(self):
filters = {"company": "_Test Company UAE VAT"}
total_emiratewise = get_total_emiratewise(filters)
amounts_by_emirate = {}
for data in total_emiratewise:
emirate, amount, vat = data
amounts_by_emirate[emirate] = {
"raw_amount": amount,
"raw_vat_amount": vat,
}
self.assertEqual(amounts_by_emirate["Sharjah"]["raw_amount"],100)
self.assertEqual(amounts_by_emirate["Sharjah"]["raw_vat_amount"],5)
self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"],200)
self.assertEqual(amounts_by_emirate["Dubai"]["raw_vat_amount"],10)
self.assertEqual(get_tourist_tax_return_total(filters),100)
self.assertEqual(get_tourist_tax_return_tax(filters),2)
self.assertEqual(get_zero_rated_total(filters),100)
self.assertEqual(get_exempt_total(filters),100)
self.assertEqual(get_standard_rated_expenses_total(filters),250)
self.assertEqual(get_standard_rated_expenses_tax(filters),1)
def make_company(company_name, abbr):
if not frappe.db.exists("Company", company_name):
company = frappe.get_doc({
"doctype": "Company",
"company_name": company_name,
"abbr": abbr,
"default_currency": "AED",
"country": "United Arab Emirates",
"create_chart_of_accounts_based_on": "Standard Template",
})
company.insert()
else:
company = frappe.get_doc("Company", company_name)
company.create_default_warehouses()
if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": company.name}):
company.create_default_cost_center()
company.save()
return company
def set_vat_accounts():
if not frappe.db.exists("UAE VAT Settings", "_Test Company UAE VAT"):
vat_accounts = frappe.get_all(
"Account",
fields=["name"],
filters = {
"company": "_Test Company UAE VAT",
"is_group": 0,
"account_type": "Tax"
}
)
uae_vat_accounts = []
for account in vat_accounts:
uae_vat_accounts.append({
"doctype": "UAE VAT Account",
"account": account.name
})
frappe.get_doc({
"company": "_Test Company UAE VAT",
"uae_vat_accounts": uae_vat_accounts,
"doctype": "UAE VAT Settings",
}).insert()
def make_customer():
if not frappe.db.exists("Customer", "_Test UAE Customer"):
customer = frappe.get_doc({
"doctype": "Customer",
"customer_name": "_Test UAE Customer",
"customer_type": "Company",
})
customer.insert()
else:
customer = frappe.get_doc("Customer", "_Test UAE Customer")
def make_supplier():
if not frappe.db.exists("Supplier", "_Test UAE Supplier"):
frappe.get_doc({
"supplier_group": "Local",
"supplier_name": "_Test UAE Supplier",
"supplier_type": "Individual",
"doctype": "Supplier",
}).insert()
def create_warehouse(warehouse_name, properties=None, company=None):
if not company:
company = "_Test Company"
warehouse_id = erpnext.encode_company_abbr(warehouse_name, company)
if not frappe.db.exists("Warehouse", warehouse_id):
warehouse = frappe.new_doc("Warehouse")
warehouse.warehouse_name = warehouse_name
warehouse.parent_warehouse = "All Warehouses - _TCUV"
warehouse.company = company
warehouse.account = get_warehouse_account(warehouse_name, company)
if properties:
warehouse.update(properties)
warehouse.save()
return warehouse.name
else:
return warehouse_id
def make_item(item_code, properties=None):
if frappe.db.exists("Item", item_code):
return frappe.get_doc("Item", item_code)
item = frappe.get_doc({
"doctype": "Item",
"item_code": item_code,
"item_name": item_code,
"description": item_code,
"item_group": "Products"
})
if properties:
item.update(properties)
item.insert()
return item
def make_sales_invoices():
def make_sales_invoices_wrapper(emirate, item, tax = True, tourist_tax= False):
si = create_sales_invoice(
company="_Test Company UAE VAT",
customer = '_Test UAE Customer',
currency = 'AED',
warehouse = 'Finished Goods - _TCUV',
debit_to = 'Debtors - _TCUV',
income_account = 'Sales - _TCUV',
expense_account = 'Cost of Goods Sold - _TCUV',
cost_center = 'Main - _TCUV',
item = item,
do_not_save=1
)
si.vat_emirate = emirate
if tax:
si.append(
"taxes", {
"charge_type": "On Net Total",
"account_head": "VAT 5% - _TCUV",
"cost_center": "Main - _TCUV",
"description": "VAT 5% @ 5.0",
"rate": 5.0
}
)
if tourist_tax:
si.tourist_tax_return = 2
si.submit()
#Define Item Names
uae_item = "_Test UAE VAT Item"
uae_exempt_item = "_Test UAE VAT Exempt Item"
uae_zero_rated_item = "_Test UAE VAT Zero Rated Item"
#Sales Invoice with standard rated expense in Dubai
make_sales_invoices_wrapper('Dubai', uae_item)
#Sales Invoice with standard rated expense in Sharjah
make_sales_invoices_wrapper('Sharjah', uae_item)
#Sales Invoice with Tourist Tax Return
make_sales_invoices_wrapper('Dubai', uae_item, True, True)
#Sales Invoice with Exempt Item
make_sales_invoices_wrapper('Sharjah', uae_exempt_item, False)
#Sales Invoice with Zero Rated Item
make_sales_invoices_wrapper('Sharjah', uae_zero_rated_item, False)
def create_purchase_invoices():
pi = make_purchase_invoice(
company="_Test Company UAE VAT",
supplier = '_Test UAE Supplier',
supplier_warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV',
warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV',
currency = 'AED',
cost_center = 'Main - _TCUV',
expense_account = 'Cost of Goods Sold - _TCUV',
item = "_Test UAE VAT Item",
do_not_save=1,
uom = "Nos"
)
pi.append("taxes", {
"charge_type": "On Net Total",
"account_head": "VAT 5% - _TCUV",
"cost_center": "Main - _TCUV",
"description": "VAT 5% @ 5.0",
"rate": 5.0
})
pi.recoverable_standard_rated_expenses = 1
pi.submit()

View File

@ -0,0 +1,77 @@
{%
var report_columns = report.get_columns_for_print();
report_columns = report_columns.filter(col => !col.hidden);
%}
<style>
.print-format {
padding: 10mm;
font-size: 8.0pt !important;
font-family: Tahoma, sans-serif;
}
</style>
<h1 style="margin-top:0; text-align: center;">{%= __(report.report_name) %}</h1>
<h3 style="margin-top:0; font-weight:500">{%= __("VAT on Sales and All Other Outputs") %}</h2>
<table class="table table-bordered">
<thead>
<th style="width: 13">{%= report_columns[0].label %}</th>
<th style="width: {%= 100 - (report_columns.length - 1) * 13%}%">{%= report_columns[1].label %}</th>
{% for (let i=2; i<report_columns.length; i++) { %}
<th style="width: 13">{%= report_columns[i].label %}</th>
{% } %}
</thead>
<tbody>
{% for (let j=1; j<12; j++) { %}
{%
var row = data[j];
%}
<tr >
{% for (let i=0; i<report_columns.length; i++) { %}
<td >
{% const fieldname = report_columns[i].fieldname; %}
{% if (!is_null(row[fieldname])) { %}
{%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
{% } %}
</td>
{% } %}
</tr>
{% } %}
</tbody>
</table>
<h3 style="margin-top:0; font-weight:500">{%= __("VAT on Expenses and All Other Inputs") %}</h2>
<table class="table table-bordered">
<thead>
<th style="width: 13">{%= report_columns[0].label %}</th>
<th style="width: {%= 100 - (report_columns.length - 1) * 13%}%">{%= report_columns[1].label %}</th>
{% for (let i=2; i<report_columns.length; i++) { %}
<th style="width: 13">{%= report_columns[i].label %}</th>
{% } %}
</thead>
<tbody>
{% for (let j=14; j<data.length; j++) { %}
{%
var row = data[j];
%}
<tr >
{% for (let i=0; i<report_columns.length; i++) { %}
<td >
{% const fieldname = report_columns[i].fieldname; %}
{% if (!is_null(row[fieldname])) { %}
{%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
{% } %}
</td>
{% } %}
</tr>
{% } %}
</tbody>
</table>

View File

@ -0,0 +1,40 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["UAE VAT 201"] = {
"filters": [
{
"fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"reqd": 1,
"default": frappe.defaults.get_user_default("Company")
},
{
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -3),
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.get_today()
},
],
"formatter": function(value, row, column, data, default_formatter) {
if (data
&& (data.legend=='VAT on Sales and All Other Outputs' || data.legend=='VAT on Expenses and All Other Inputs')
&& data.legend==value) {
value = $(`<span>${value}</span>`);
var $value = $(value).css("font-weight", "bold");
value = $value.wrap("<p></p>").parent().html();
}
return value;
},
};

View File

@ -0,0 +1,22 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2020-09-10 08:51:02.298482",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"modified": "2020-09-10 08:51:02.298482",
"modified_by": "Administrator",
"module": "Regional",
"name": "UAE VAT 201",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "GL Entry",
"report_name": "UAE VAT 201",
"report_type": "Script Report",
"roles": []
}

View File

@ -0,0 +1,339 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
def execute(filters=None):
columns = get_columns()
data, emirates, amounts_by_emirate = get_data(filters)
return columns, data
def get_columns():
"""Creates a list of dictionaries that are used to generate column headers of the data table."""
return [
{
"fieldname": "no",
"label": _("No"),
"fieldtype": "Data",
"width": 50
},
{
"fieldname": "legend",
"label": _("Legend"),
"fieldtype": "Data",
"width": 300
},
{
"fieldname": "amount",
"label": _("Amount (AED)"),
"fieldtype": "Currency",
"width": 125,
},
{
"fieldname": "vat_amount",
"label": _("VAT Amount (AED)"),
"fieldtype": "Currency",
"width": 150,
}
]
def get_data(filters = None):
"""Returns the list of dictionaries. Each dictionary is a row in the datatable and chart data."""
data = []
emirates, amounts_by_emirate = append_vat_on_sales(data, filters)
append_vat_on_expenses(data, filters)
return data, emirates, amounts_by_emirate
def append_vat_on_sales(data, filters):
"""Appends Sales and All Other Outputs."""
append_data(data, '', _('VAT on Sales and All Other Outputs'), '', '')
emirates, amounts_by_emirate = standard_rated_expenses_emiratewise(data, filters)
append_data(data, '2',
_('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'),
frappe.format((-1) * get_tourist_tax_return_total(filters), 'Currency'),
frappe.format((-1) * get_tourist_tax_return_tax(filters), 'Currency'))
append_data(data, '3', _('Supplies subject to the reverse charge provision'),
frappe.format(get_reverse_charge_total(filters), 'Currency'),
frappe.format(get_reverse_charge_tax(filters), 'Currency'))
append_data(data, '4', _('Zero Rated'),
frappe.format(get_zero_rated_total(filters), 'Currency'), "-")
append_data(data, '5', _('Exempt Supplies'),
frappe.format(get_exempt_total(filters), 'Currency'),"-")
append_data(data, '', '', '', '')
return emirates, amounts_by_emirate
def standard_rated_expenses_emiratewise(data, filters):
"""Append emiratewise standard rated expenses and vat."""
total_emiratewise = get_total_emiratewise(filters)
emirates = get_emirates()
amounts_by_emirate = {}
for emirate, amount, vat in total_emiratewise:
amounts_by_emirate[emirate] = {
"legend": emirate,
"raw_amount": amount,
"raw_vat_amount": vat,
"amount": frappe.format(amount, 'Currency'),
"vat_amount": frappe.format(vat, 'Currency'),
}
amounts_by_emirate = append_emiratewise_expenses(data, emirates, amounts_by_emirate)
return emirates, amounts_by_emirate
def append_emiratewise_expenses(data, emirates, amounts_by_emirate):
"""Append emiratewise standard rated expenses and vat."""
for no, emirate in enumerate(emirates, 97):
if emirate in amounts_by_emirate:
amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(no))
amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate)
data.append(amounts_by_emirate[emirate])
else:
append_data(data, _('1{0}').format(chr(no)),
_('Standard rated supplies in {0}').format(emirate),
frappe.format(0, 'Currency'), frappe.format(0, 'Currency'))
return amounts_by_emirate
def append_vat_on_expenses(data, filters):
"""Appends Expenses and All Other Inputs."""
append_data(data, '', _('VAT on Expenses and All Other Inputs'), '', '')
append_data(data, '9', _('Standard Rated Expenses'),
frappe.format(get_standard_rated_expenses_total(filters), 'Currency'),
frappe.format(get_standard_rated_expenses_tax(filters), 'Currency'))
append_data(data, '10', _('Supplies subject to the reverse charge provision'),
frappe.format(get_reverse_charge_recoverable_total(filters), 'Currency'),
frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency'))
def append_data(data, no, legend, amount, vat_amount):
"""Returns data with appended value."""
data.append({"no": no, "legend":legend, "amount": amount, "vat_amount": vat_amount})
def get_total_emiratewise(filters):
"""Returns Emiratewise Amount and Taxes."""
conditions = get_conditions(filters)
try:
return frappe.db.sql("""
select
s.vat_emirate as emirate, sum(i.base_amount) as total, sum(s.total_taxes_and_charges)
from
`tabSales Invoice Item` i inner join `tabSales Invoice` s
on
i.parent = s.name
where
s.docstatus = 1 and i.is_exempt != 1 and i.is_zero_rated != 1
{where_conditions}
group by
s.vat_emirate;
""".format(where_conditions=conditions), filters)
except (IndexError, TypeError):
return 0
def get_emirates():
"""Returns a List of emirates in the order that they are to be displayed."""
return [
'Abu Dhabi',
'Dubai',
'Sharjah',
'Ajman',
'Umm Al Quwain',
'Ras Al Khaimah',
'Fujairah'
]
def get_filters(filters):
"""The conditions to be used to filter data to calculate the total sale."""
query_filters = []
if filters.get("company"):
query_filters.append(["company", '=', filters['company']])
if filters.get("from_date"):
query_filters.append(["posting_date", '>=', filters['from_date']])
if filters.get("from_date"):
query_filters.append(["posting_date", '<=', filters['to_date']])
return query_filters
def get_reverse_charge_total(filters):
"""Returns the sum of the total of each Purchase invoice made."""
query_filters = get_filters(filters)
query_filters.append(['reverse_charge', '=', 'Y'])
query_filters.append(['docstatus', '=', 1])
try:
return frappe.db.get_all('Purchase Invoice',
filters = query_filters,
fields = ['sum(total)'],
as_list=True,
limit = 1
)[0][0] or 0
except (IndexError, TypeError):
return 0
def get_reverse_charge_tax(filters):
"""Returns the sum of the tax of each Purchase invoice made."""
conditions = get_conditions_join(filters)
return frappe.db.sql("""
select sum(debit) from
`tabPurchase Invoice` p inner join `tabGL Entry` gl
on
gl.voucher_no = p.name
where
p.reverse_charge = "Y"
and p.docstatus = 1
and gl.docstatus = 1
and account in (select account from `tabUAE VAT Account` where parent=%(company)s)
{where_conditions} ;
""".format(where_conditions=conditions), filters)[0][0] or 0
def get_reverse_charge_recoverable_total(filters):
"""Returns the sum of the total of each Purchase invoice made with recoverable reverse charge."""
query_filters = get_filters(filters)
query_filters.append(['reverse_charge', '=', 'Y'])
query_filters.append(['recoverable_reverse_charge', '>', '0'])
query_filters.append(['docstatus', '=', 1])
try:
return frappe.db.get_all('Purchase Invoice',
filters = query_filters,
fields = ['sum(total)'],
as_list=True,
limit = 1
)[0][0] or 0
except (IndexError, TypeError):
return 0
def get_reverse_charge_recoverable_tax(filters):
"""Returns the sum of the tax of each Purchase invoice made."""
conditions = get_conditions_join(filters)
return frappe.db.sql("""
select
sum(debit * p.recoverable_reverse_charge / 100)
from
`tabPurchase Invoice` p inner join `tabGL Entry` gl
on
gl.voucher_no = p.name
where
p.reverse_charge = "Y"
and p.docstatus = 1
and p.recoverable_reverse_charge > 0
and gl.docstatus = 1
and account in (select account from `tabUAE VAT Account` where parent=%(company)s)
{where_conditions} ;
""".format(where_conditions=conditions), filters)[0][0] or 0
def get_conditions_join(filters):
"""The conditions to be used to filter data to calculate the total vat."""
conditions = ""
for opts in (("company", " and p.company=%(company)s"),
("from_date", " and p.posting_date>=%(from_date)s"),
("to_date", " and p.posting_date<=%(to_date)s")):
if filters.get(opts[0]):
conditions += opts[1]
return conditions
def get_standard_rated_expenses_total(filters):
"""Returns the sum of the total of each Purchase invoice made with recoverable reverse charge."""
query_filters = get_filters(filters)
query_filters.append(['recoverable_standard_rated_expenses', '>', 0])
query_filters.append(['docstatus', '=', 1])
try:
return frappe.db.get_all('Purchase Invoice',
filters = query_filters,
fields = ['sum(total)'],
as_list=True,
limit = 1
)[0][0] or 0
except (IndexError, TypeError):
return 0
def get_standard_rated_expenses_tax(filters):
"""Returns the sum of the tax of each Purchase invoice made."""
query_filters = get_filters(filters)
query_filters.append(['recoverable_standard_rated_expenses', '>', 0])
query_filters.append(['docstatus', '=', 1])
try:
return frappe.db.get_all('Purchase Invoice',
filters = query_filters,
fields = ['sum(recoverable_standard_rated_expenses)'],
as_list=True,
limit = 1
)[0][0] or 0
except (IndexError, TypeError):
return 0
def get_tourist_tax_return_total(filters):
"""Returns the sum of the total of each Sales invoice with non zero tourist_tax_return."""
query_filters = get_filters(filters)
query_filters.append(['tourist_tax_return', '>', 0])
query_filters.append(['docstatus', '=', 1])
try:
return frappe.db.get_all('Sales Invoice',
filters = query_filters,
fields = ['sum(total)'],
as_list=True,
limit = 1
)[0][0] or 0
except (IndexError, TypeError):
return 0
def get_tourist_tax_return_tax(filters):
"""Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return."""
query_filters = get_filters(filters)
query_filters.append(['tourist_tax_return', '>', 0])
query_filters.append(['docstatus', '=', 1])
try:
return frappe.db.get_all('Sales Invoice',
filters = query_filters,
fields = ['sum(tourist_tax_return)'],
as_list=True,
limit = 1
)[0][0] or 0
except (IndexError, TypeError):
return 0
def get_zero_rated_total(filters):
"""Returns the sum of each Sales Invoice Item Amount which is zero rated."""
conditions = get_conditions(filters)
try:
return frappe.db.sql("""
select
sum(i.base_amount) as total
from
`tabSales Invoice Item` i inner join `tabSales Invoice` s
on
i.parent = s.name
where
s.docstatus = 1 and i.is_zero_rated = 1
{where_conditions} ;
""".format(where_conditions=conditions), filters)[0][0] or 0
except (IndexError, TypeError):
return 0
def get_exempt_total(filters):
"""Returns the sum of each Sales Invoice Item Amount which is Vat Exempt."""
conditions = get_conditions(filters)
try:
return frappe.db.sql("""
select
sum(i.base_amount) as total
from
`tabSales Invoice Item` i inner join `tabSales Invoice` s
on
i.parent = s.name
where
s.docstatus = 1 and i.is_exempt = 1
{where_conditions} ;
""".format(where_conditions=conditions), filters)[0][0] or 0
except (IndexError, TypeError):
return 0
def get_conditions(filters):
"""The conditions to be used to filter data to calculate the total sale."""
conditions = ""
for opts in (("company", " and company=%(company)s"),
("from_date", " and posting_date>=%(from_date)s"),
("to_date", " and posting_date<=%(to_date)s")):
if filters.get(opts[0]):
conditions += opts[1]
return conditions

View File

@ -5,24 +5,30 @@ from __future__ import unicode_literals
import frappe, os, json
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.permissions import add_permission, update_permission_property
from erpnext.setup.setup_wizard.operations.taxes_setup import create_sales_tax
def setup(company=None, patch=True):
make_custom_fields()
add_print_formats()
add_custom_roles_for_reports()
add_permissions()
if company:
create_sales_tax(company)
def make_custom_fields():
is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated',
fieldtype='Check', fetch_from='item_code.is_zero_rated', insert_after='description',
print_hide=1)
is_exempt = dict(fieldname='is_exempt', label='Is Exempt',
fieldtype='Check', fetch_from='item_code.is_exempt', insert_after='is_zero_rated',
print_hide=1)
invoice_fields = [
dict(fieldname='vat_section', label='VAT Details', fieldtype='Section Break',
insert_after='group_same_items', print_hide=1, collapsible=1),
dict(fieldname='permit_no', label='Permit Number',
fieldtype='Data', insert_after='vat_section', print_hide=1),
dict(fieldname='reverse_charge_applicable', label='Reverse Charge Applicable',
fieldtype='Select', insert_after='permit_no', print_hide=1,
options='Y\nN', default='N')
]
purchase_invoice_fields = [
@ -31,7 +37,16 @@ def make_custom_fields():
fetch_from='company.tax_id', print_hide=1),
dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
fieldtype='Read Only', insert_after='supplier_name',
fetch_from='supplier.supplier_name_in_arabic', print_hide=1)
fetch_from='supplier.supplier_name_in_arabic', print_hide=1),
dict(fieldname='recoverable_standard_rated_expenses', print_hide=1, default='0',
label='Recoverable Standard Rated Expenses (AED)', insert_after='permit_no',
fieldtype='Currency', ),
dict(fieldname='reverse_charge', label='Reverse Charge Applicable',
fieldtype='Select', insert_after='recoverable_standard_rated_expenses', print_hide=1,
options='Y\nN', default='N'),
dict(fieldname='recoverable_reverse_charge', label='Recoverable Reverse Charge (Percentage)',
insert_after='reverse_charge', fieldtype='Percent', print_hide=1,
depends_on="eval:doc.reverse_charge=='Y'", default='100.000'),
]
sales_invoice_fields = [
@ -41,6 +56,11 @@ def make_custom_fields():
dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
fieldtype='Read Only', insert_after='customer_name',
fetch_from='customer.customer_name_in_arabic', print_hide=1),
dict(fieldname='vat_emirate', label='VAT Emirate', insert_after='permit_no', fieldtype='Select',
options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain',
fetch_from='company_address.emirate'),
dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)',
insert_after='vat_emirate', fieldtype='Currency', print_hide=1, default='0'),
]
invoice_item_fields = [
@ -67,6 +87,12 @@ def make_custom_fields():
'Item': [
dict(fieldname='tax_code', label='Tax Code',
fieldtype='Data', insert_after='item_group'),
dict(fieldname='is_zero_rated', label='Is Zero Rated',
fieldtype='Check', insert_after='tax_code',
print_hide=1),
dict(fieldname='is_exempt', label='Is Exempt',
fieldtype='Check', insert_after='is_zero_rated',
print_hide=1)
],
'Customer': [
dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
@ -76,13 +102,17 @@ def make_custom_fields():
dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
fieldtype='Data', insert_after='supplier_name'),
],
'Address': [
dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state',
options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain')
],
'Purchase Invoice': purchase_invoice_fields + invoice_fields,
'Purchase Order': purchase_invoice_fields + invoice_fields,
'Purchase Receipt': purchase_invoice_fields + invoice_fields,
'Sales Invoice': sales_invoice_fields + invoice_fields,
'Sales Order': sales_invoice_fields + invoice_fields,
'Delivery Note': sales_invoice_fields + invoice_fields,
'Sales Invoice Item': invoice_item_fields + delivery_date_field,
'Sales Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
'Purchase Invoice Item': invoice_item_fields,
'Sales Order Item': invoice_item_fields,
'Delivery Note Item': invoice_item_fields,
@ -101,3 +131,25 @@ def add_print_formats():
frappe.db.sql(""" update `tabPrint Format` set disabled = 0 where
name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """)
def add_custom_roles_for_reports():
"""Add Access Control to UAE VAT 201."""
if not frappe.db.get_value('Custom Role', dict(report='UAE VAT 201')):
frappe.get_doc(dict(
doctype='Custom Role',
report='UAE VAT 201',
roles= [
dict(role='Accounts User'),
dict(role='Accounts Manager'),
dict(role='Auditor')
]
)).insert()
def add_permissions():
"""Add Permissions for UAE VAT Settings and UAE VAT Account."""
for doctype in ('UAE VAT Settings', 'UAE VAT Account'):
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)

View File

@ -1,6 +1,8 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import flt
from frappe import _
import erpnext
from frappe.utils import flt, round_based_on_smallest_currency_fraction, money_in_words
from erpnext.controllers.taxes_and_totals import get_itemised_tax
from six import iteritems
@ -26,4 +28,134 @@ def update_itemised_tax_data(doc):
row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
def get_account_currency(account):
"""Helper function to get account currency."""
if not account:
return
def generator():
account_currency, company = frappe.get_cached_value(
"Account",
account,
["account_currency",
"company"]
)
if not account_currency:
account_currency = frappe.get_cached_value('Company', company, "default_currency")
return account_currency
return frappe.local_cache("account_currency", account, generator)
def get_tax_accounts(company):
"""Get the list of tax accounts for a specific company."""
tax_accounts_dict = frappe._dict()
tax_accounts_list = frappe.get_all("UAE VAT Account",
filters={"parent": company},
fields=["Account"]
)
if not tax_accounts_list and not frappe.flags.in_test:
frappe.throw(_('Please set Vat Accounts for Company: "{0}" in UAE VAT Settings').format(company))
for tax_account in tax_accounts_list:
for account, name in tax_account.items():
tax_accounts_dict[name] = name
return tax_accounts_dict
def update_grand_total_for_rcm(doc, method):
"""If the Reverse Charge is Applicable subtract the tax amount from the grand total and update in the form."""
country = frappe.get_cached_value('Company', doc.company, 'country')
if country != 'United Arab Emirates':
return
if not doc.total_taxes_and_charges:
return
if doc.reverse_charge == 'Y':
tax_accounts = get_tax_accounts(doc.company)
base_vat_tax = 0
vat_tax = 0
for tax in doc.get('taxes'):
if tax.category not in ("Total", "Valuation and Total"):
continue
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts:
base_vat_tax += tax.base_tax_amount_after_discount_amount
vat_tax += tax.tax_amount_after_discount_amount
doc.taxes_and_charges_added -= vat_tax
doc.total_taxes_and_charges -= vat_tax
doc.base_taxes_and_charges_added -= base_vat_tax
doc.base_total_taxes_and_charges -= base_vat_tax
update_totals(vat_tax, base_vat_tax, doc)
def update_totals(vat_tax, base_vat_tax, doc):
"""Update the grand total values in the form."""
doc.base_grand_total -= base_vat_tax
doc.grand_total -= vat_tax
if doc.meta.get_field("rounded_total"):
if doc.is_rounded_total_disabled():
doc.outstanding_amount = doc.grand_total
else:
doc.rounded_total = round_based_on_smallest_currency_fraction(doc.grand_total,
doc.currency, doc.precision("rounded_total"))
doc.rounding_adjustment = flt(doc.rounded_total - doc.grand_total,
doc.precision("rounding_adjustment"))
doc.outstanding_amount = doc.rounded_total or doc.grand_total
doc.in_words = money_in_words(doc.grand_total, doc.currency)
doc.base_in_words = money_in_words(doc.base_grand_total, erpnext.get_company_currency(doc.company))
doc.set_payment_schedule()
def make_regional_gl_entries(gl_entries, doc):
"""Hooked to make_regional_gl_entries in Purchase Invoice.It appends the region specific general ledger entries to the list of GL Entries."""
country = frappe.get_cached_value('Company', doc.company, 'country')
if country != 'United Arab Emirates':
return gl_entries
if doc.reverse_charge == 'Y':
tax_accounts = get_tax_accounts(doc.company)
for tax in doc.get('taxes'):
if tax.category not in ("Total", "Valuation and Total"):
continue
gl_entries = make_gl_entry(tax, gl_entries, doc, tax_accounts)
return gl_entries
def make_gl_entry(tax, gl_entries, doc, tax_accounts):
dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts:
account_currency = get_account_currency(tax.account_head)
gl_entries.append(doc.get_gl_dict({
"account": tax.account_head,
"cost_center": tax.cost_center,
"posting_date": doc.posting_date,
"against": doc.supplier,
dr_or_cr: tax.base_tax_amount_after_discount_amount,
dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \
if account_currency==doc.company_currency \
else tax.tax_amount_after_discount_amount
}, account_currency, item=tax
))
return gl_entries
def validate_returns(doc, method):
"""Standard Rated expenses should not be set when Reverse Charge Applicable is set."""
country = frappe.get_cached_value('Company', doc.company, 'country')
if country != 'United Arab Emirates':
return
if doc.reverse_charge == 'Y' and flt(doc.recoverable_standard_rated_expenses) != 0:
frappe.throw(_(
"Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y"
))