Merge branch 'develop' into naming-series-proj
This commit is contained in:
commit
2afc0bd87b
@ -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",
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-03-25 10:53:52",
|
||||
@ -503,7 +504,7 @@
|
||||
"idx": 176,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-06-02 18:15:46.955697",
|
||||
"modified": "2020-10-30 13:56:01.121995",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry",
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2016-06-01 14:38:51.012597",
|
||||
@ -587,7 +588,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-02 13:39:43.383705",
|
||||
"modified": "2020-10-30 13:56:20.007336",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2020-01-24 15:29:29.933693",
|
||||
@ -1580,7 +1581,7 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-28 16:51:24.641755",
|
||||
"modified": "2020-10-30 13:56:51.056083",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice",
|
||||
|
@ -39,6 +39,7 @@ class POSInvoice(SalesInvoice):
|
||||
self.validate_serialised_or_batched_item()
|
||||
self.validate_stock_availablility()
|
||||
self.validate_return_items_qty()
|
||||
self.validate_non_stock_items()
|
||||
self.set_status()
|
||||
self.set_account_for_mode_of_payment()
|
||||
self.validate_pos()
|
||||
@ -174,6 +175,14 @@ class POSInvoice(SalesInvoice):
|
||||
_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
|
||||
.format(d.idx, bold_serial_no, bold_return_against)
|
||||
)
|
||||
|
||||
def validate_non_stock_items(self):
|
||||
for d in self.get("items"):
|
||||
is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
|
||||
if not is_stock_item:
|
||||
frappe.throw(_("Row #{}: Item {} is a non stock item. You can only include stock items in a POS Invoice. ").format(
|
||||
d.idx, frappe.bold(d.item_code)
|
||||
), title=_("Invalid Item"))
|
||||
|
||||
def validate_mode_of_payment(self):
|
||||
if len(self.payments) == 0:
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
@ -1334,7 +1335,8 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"modified": "2020-09-21 12:22:09.164068",
|
||||
"links": [],
|
||||
"modified": "2020-10-30 13:57:18.266978",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
@ -1396,4 +1398,4 @@
|
||||
"timeline_field": "supplier",
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
@ -1003,7 +1003,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 "",
|
||||
@ -1045,7 +1045,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",
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-24 19:29:05",
|
||||
@ -1955,7 +1956,7 @@
|
||||
"idx": 181,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-09 15:59:57.544736",
|
||||
"modified": "2020-10-30 13:57:45.086303",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
@ -1105,7 +1106,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-30 11:39:37.388249",
|
||||
"modified": "2020-10-30 13:58:14.697921",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:45",
|
||||
@ -807,7 +808,7 @@
|
||||
"idx": 29,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-01 20:56:17.932007",
|
||||
"modified": "2020-10-30 13:58:33.043971",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation",
|
||||
|
@ -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'
|
||||
|
@ -32,7 +32,7 @@ class LeaveEncashment(Document):
|
||||
additional_salary.employee = self.employee
|
||||
earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component")
|
||||
if not earning_component:
|
||||
frappe.throw(_("Please set Earning Component for Leave type: {0}.".format(self.leave_type)))
|
||||
frappe.throw(_("Please set Earning Component for Leave type: {0}.").format(self.leave_type))
|
||||
additional_salary.salary_component = earning_component
|
||||
additional_salary.payroll_date = self.encashment_date
|
||||
additional_salary.amount = self.encashment_amount
|
||||
@ -98,7 +98,11 @@ class LeaveEncashment(Document):
|
||||
create_leave_ledger_entry(self, args, submit)
|
||||
|
||||
# create reverse entry for expired leaves
|
||||
to_date = self.get_leave_allocation().get('to_date')
|
||||
leave_allocation = self.get_leave_allocation()
|
||||
if not leave_allocation:
|
||||
return
|
||||
|
||||
to_date = leave_allocation.get('to_date')
|
||||
if to_date < getdate(nowdate()):
|
||||
args = frappe._dict(
|
||||
leaves=self.encashable_days,
|
||||
|
@ -127,6 +127,7 @@ def create_loan(source_name, target_doc=None, submit=0):
|
||||
target_doc.loan_account = account_details.loan_account
|
||||
target_doc.interest_income_account = account_details.interest_income_account
|
||||
target_doc.penalty_income_account = account_details.penalty_income_account
|
||||
target_doc.loan_application = source_name
|
||||
|
||||
|
||||
doclist = get_mapped_doc("Loan Application", source_name, {
|
||||
|
@ -70,7 +70,7 @@ class Membership(Document):
|
||||
settings = frappe.get_doc("Membership Settings")
|
||||
|
||||
if not member.customer:
|
||||
frappe.throw(_("No customer linked to member {}", [member.name]))
|
||||
frappe.throw(_("No customer linked to member {0}").format(frappe.bold(self.member)))
|
||||
|
||||
if not settings.debit_account:
|
||||
frappe.throw(_("You need to set <b>Debit Account</b> in Membership Settings"))
|
||||
|
@ -51,7 +51,7 @@ def generate_encashment_leave_ledger_entries():
|
||||
|
||||
for encashment in leave_encashments:
|
||||
if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}):
|
||||
frappe.get_doc("Leave Enchashment", encashment).create_leave_ledger_entry()
|
||||
frappe.get_doc("Leave Encashment", encashment).create_leave_ledger_entry()
|
||||
|
||||
def generate_expiry_allocation_ledger_entries():
|
||||
''' fix ledger entries for missing leave allocation transaction '''
|
||||
|
@ -344,9 +344,13 @@ class PayrollEntry(Document):
|
||||
employees_to_mark_attendance = []
|
||||
days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
|
||||
for employee_detail in self.employees:
|
||||
days_holiday = self.get_count_holidays_of_employee(employee_detail.employee)
|
||||
days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee)
|
||||
days_in_payroll = date_diff(self.end_date, self.start_date) + 1
|
||||
employee_joining_date = frappe.db.get_value("Employee", employee_detail.employee, 'date_of_joining')
|
||||
start_date = self.start_date
|
||||
if employee_joining_date > getdate(self.start_date):
|
||||
start_date = employee_joining_date
|
||||
days_holiday = self.get_count_holidays_of_employee(employee_detail.employee, start_date)
|
||||
days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee, start_date)
|
||||
days_in_payroll = date_diff(self.end_date, start_date) + 1
|
||||
if days_in_payroll > days_holiday + days_attendance_marked:
|
||||
employees_to_mark_attendance.append({
|
||||
"employee": employee_detail.employee,
|
||||
@ -354,22 +358,25 @@ class PayrollEntry(Document):
|
||||
})
|
||||
return employees_to_mark_attendance
|
||||
|
||||
def get_count_holidays_of_employee(self, employee):
|
||||
def get_count_holidays_of_employee(self, employee, start_date):
|
||||
holiday_list = get_holiday_list_for_employee(employee)
|
||||
holidays = 0
|
||||
if holiday_list:
|
||||
days = frappe.db.sql("""select count(*) from tabHoliday where
|
||||
parent=%s and holiday_date between %s and %s""", (holiday_list,
|
||||
self.start_date, self.end_date))
|
||||
start_date, self.end_date))
|
||||
if days and days[0][0]:
|
||||
holidays = days[0][0]
|
||||
return holidays
|
||||
|
||||
def get_count_employee_attendance(self, employee):
|
||||
def get_count_employee_attendance(self, employee, start_date):
|
||||
marked_days = 0
|
||||
attendances = frappe.db.sql("""select count(*) from tabAttendance where
|
||||
employee=%s and docstatus=1 and attendance_date between %s and %s""",
|
||||
(employee, self.start_date, self.end_date))
|
||||
attendances = frappe.get_all("Attendance",
|
||||
fields = ["count(*)"],
|
||||
filters = {
|
||||
"employee": employee,
|
||||
"attendance_date": ('between', [start_date, self.end_date])
|
||||
}, as_list=1)
|
||||
if attendances and attendances[0][0]:
|
||||
marked_days = attendances[0][0]
|
||||
return marked_days
|
||||
|
@ -240,7 +240,6 @@ class SalarySlip(TransactionBase):
|
||||
self.absent_days += unmarked_days #will be treated as absent
|
||||
self.payment_days -= unmarked_days
|
||||
if include_holidays_in_total_working_days:
|
||||
self.absent_days -= len(holidays)
|
||||
for holiday in holidays:
|
||||
if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }):
|
||||
self.payment_days += 1
|
||||
|
@ -47,33 +47,39 @@ def execute(filters=None):
|
||||
"fieldtype": "Int",
|
||||
"fieldname": "employee_account_no",
|
||||
"width": 50
|
||||
},
|
||||
{
|
||||
}
|
||||
]
|
||||
|
||||
if frappe.db.has_column('Employee', 'ifsc_code'):
|
||||
columns.append({
|
||||
"label": _("IFSC Code"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "bank_code",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Currency"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "currency",
|
||||
"width": 50
|
||||
},
|
||||
{
|
||||
"label": _("Net Salary Amount"),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"fieldname": "amount",
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
columns += [{
|
||||
"label": _("Currency"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "currency",
|
||||
"width": 50
|
||||
},
|
||||
{
|
||||
"label": _("Net Salary Amount"),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"fieldname": "amount",
|
||||
"width": 100
|
||||
}]
|
||||
|
||||
data = []
|
||||
|
||||
accounts = get_bank_accounts()
|
||||
payroll_entries = get_payroll_entries(accounts, filters)
|
||||
salary_slips = get_salary_slips(payroll_entries)
|
||||
get_emp_bank_ifsc_code(salary_slips)
|
||||
|
||||
if frappe.db.has_column('Employee', 'ifsc_code'):
|
||||
get_emp_bank_ifsc_code(salary_slips)
|
||||
|
||||
for salary in salary_slips:
|
||||
if salary.bank_name and salary.bank_account_no and salary.debit_acc_no and salary.status in ["Submitted", "Paid"]:
|
||||
|
@ -146,18 +146,18 @@ cur_frm.cscript.account_head = function(doc, cdt, cdn) {
|
||||
if(!d.charge_type && d.account_head){
|
||||
frappe.msgprint(__("Please select Charge Type first"));
|
||||
frappe.model.set_value(cdt, cdn, "account_head", "");
|
||||
} else if(d.account_head && d.charge_type!=="Actual") {
|
||||
} else if (d.account_head) {
|
||||
frappe.call({
|
||||
type:"GET",
|
||||
method: "erpnext.controllers.accounts_controller.get_tax_rate",
|
||||
args: {"account_head":d.account_head},
|
||||
callback: function(r) {
|
||||
frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0);
|
||||
if (d.charge_type!=="Actual") {
|
||||
frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0);
|
||||
}
|
||||
frappe.model.set_value(cdt, cdn, "description", r.message.account_name);
|
||||
}
|
||||
})
|
||||
} else if (d.charge_type == 'Actual' && d.account_head) {
|
||||
frappe.model.set_value(cdt, cdn, "description", d.account_head.split(' - ')[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
10
erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
Normal file
10
erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
Normal 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
|
@ -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
|
@ -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) {
|
||||
|
||||
// }
|
||||
});
|
@ -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
|
||||
}
|
@ -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
|
@ -516,6 +516,9 @@ def get_address_details(data, doc, company_address, billing_address):
|
||||
data.transType = 1
|
||||
data.actualToStateCode = data.toStateCode
|
||||
shipping_address = billing_address
|
||||
|
||||
if doc.gst_category == 'SEZ':
|
||||
data.toStateCode = 99
|
||||
|
||||
return data
|
||||
|
||||
|
0
erpnext/regional/report/uae_vat_201/__init__.py
Normal file
0
erpnext/regional/report/uae_vat_201/__init__.py
Normal file
239
erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
Normal file
239
erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
Normal 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()
|
77
erpnext/regional/report/uae_vat_201/uae_vat_201.html
Normal file
77
erpnext/regional/report/uae_vat_201/uae_vat_201.html
Normal 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>
|
40
erpnext/regional/report/uae_vat_201/uae_vat_201.js
Normal file
40
erpnext/regional/report/uae_vat_201/uae_vat_201.js
Normal 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;
|
||||
},
|
||||
};
|
22
erpnext/regional/report/uae_vat_201/uae_vat_201.json
Normal file
22
erpnext/regional/report/uae_vat_201/uae_vat_201.json
Normal 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": []
|
||||
}
|
339
erpnext/regional/report/uae_vat_201/uae_vat_201.py
Normal file
339
erpnext/regional/report/uae_vat_201/uae_vat_201.py
Normal 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
|
@ -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)
|
||||
|
@ -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"
|
||||
))
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-24 19:29:08",
|
||||
@ -932,7 +933,7 @@
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"max_attachments": 1,
|
||||
"modified": "2020-07-26 17:46:19.951223",
|
||||
"modified": "2020-10-30 13:58:59.212060",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation",
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-06-18 12:39:59",
|
||||
@ -1460,7 +1461,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-07 14:30:01.782617",
|
||||
"modified": "2020-10-30 13:59:18.628077",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
|
@ -844,7 +844,8 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t
|
||||
"contact_email",
|
||||
"contact_person",
|
||||
"taxes_and_charges",
|
||||
"shipping_address"
|
||||
"shipping_address",
|
||||
"terms"
|
||||
],
|
||||
"validation": {
|
||||
"docstatus": ["=", 1]
|
||||
@ -863,7 +864,10 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t
|
||||
"field_no_map": [
|
||||
"rate",
|
||||
"price_list_rate",
|
||||
"item_tax_template"
|
||||
"item_tax_template",
|
||||
"discount_percentage",
|
||||
"discount_amount",
|
||||
"pricing_rules"
|
||||
],
|
||||
"postprocess": update_item,
|
||||
"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map
|
||||
@ -917,7 +921,8 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
|
||||
"contact_email",
|
||||
"contact_person",
|
||||
"taxes_and_charges",
|
||||
"shipping_address"
|
||||
"shipping_address",
|
||||
"terms"
|
||||
],
|
||||
"validation": {
|
||||
"docstatus": ["=", 1]
|
||||
@ -937,7 +942,10 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
|
||||
"rate",
|
||||
"price_list_rate",
|
||||
"item_tax_template",
|
||||
"supplier"
|
||||
"discount_percentage",
|
||||
"discount_amount",
|
||||
"supplier",
|
||||
"pricing_rules"
|
||||
],
|
||||
"postprocess": update_item,
|
||||
"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.item_code in items_to_map
|
||||
|
@ -62,6 +62,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va
|
||||
`tabItem` item {bin_join_selection}
|
||||
WHERE
|
||||
item.disabled = 0
|
||||
AND item.is_stock_item = 1
|
||||
AND item.has_variants = 0
|
||||
AND item.is_sales_item = 1
|
||||
AND item.is_fixed_asset = 0
|
||||
|
@ -48,12 +48,8 @@ class EmailDigest(Document):
|
||||
recipients = list(filter(lambda r: r in valid_users,
|
||||
self.recipient_list.split("\n")))
|
||||
|
||||
original_user = frappe.session.user
|
||||
|
||||
if recipients:
|
||||
for user_id in recipients:
|
||||
frappe.set_user(user_id)
|
||||
frappe.set_user_lang(user_id)
|
||||
msg_for_this_recipient = self.get_msg_html()
|
||||
if msg_for_this_recipient:
|
||||
frappe.sendmail(
|
||||
@ -64,9 +60,6 @@ class EmailDigest(Document):
|
||||
reference_name = self.name,
|
||||
unsubscribe_message = _("Unsubscribe from this Email Digest"))
|
||||
|
||||
frappe.set_user(original_user)
|
||||
frappe.set_user_lang(original_user)
|
||||
|
||||
def get_msg_html(self):
|
||||
"""Build email digest content"""
|
||||
frappe.flags.ignore_account_permission = True
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-24 19:29:09",
|
||||
|
@ -4,14 +4,13 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
class ItemPriceDuplicateItem(frappe.ValidationError): pass
|
||||
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class ItemPriceDuplicateItem(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class ItemPrice(Document):
|
||||
|
||||
def validate(self):
|
||||
@ -23,7 +22,7 @@ class ItemPrice(Document):
|
||||
|
||||
def validate_item(self):
|
||||
if not frappe.db.exists("Item", self.item_code):
|
||||
frappe.throw(_("Item {0} not found").format(self.item_code))
|
||||
frappe.throw(_("Item {0} not found.").format(self.item_code))
|
||||
|
||||
def validate_dates(self):
|
||||
if self.valid_from and self.valid_upto:
|
||||
@ -38,40 +37,45 @@ class ItemPrice(Document):
|
||||
|
||||
if not price_list_details:
|
||||
link = frappe.utils.get_link_to_form('Price List', self.price_list)
|
||||
frappe.throw("The price list {0} does not exists or disabled".
|
||||
format(link))
|
||||
frappe.throw("The price list {0} does not exist or is disabled".format(link))
|
||||
|
||||
self.buying, self.selling, self.currency = price_list_details
|
||||
|
||||
def update_item_details(self):
|
||||
if self.item_code:
|
||||
self.item_name, self.item_description = frappe.db.get_value("Item",
|
||||
self.item_code,["item_name", "description"])
|
||||
self.item_name, self.item_description = frappe.db.get_value("Item", self.item_code,["item_name", "description"])
|
||||
|
||||
def check_duplicates(self):
|
||||
conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s"
|
||||
condition_data_dict = dict(item_code=self.item_code, price_list=self.price_list, name=self.name)
|
||||
conditions = """where item_code = %(item_code)s and price_list = %(price_list)s and name != %(name)s"""
|
||||
|
||||
for field in ['uom', 'valid_from',
|
||||
'valid_upto', 'packing_unit', 'customer', 'supplier']:
|
||||
for field in [
|
||||
"uom",
|
||||
"valid_from",
|
||||
"valid_upto",
|
||||
"packing_unit",
|
||||
"customer",
|
||||
"supplier",]:
|
||||
if self.get(field):
|
||||
conditions += " and {0} = %({1})s".format(field, field)
|
||||
condition_data_dict[field] = self.get(field)
|
||||
conditions += " and {0} = %({0})s ".format(field)
|
||||
else:
|
||||
conditions += "and (isnull({0}) or {0} = '')".format(field)
|
||||
|
||||
price_list_rate = frappe.db.sql("""
|
||||
SELECT price_list_rate
|
||||
FROM `tabItem Price`
|
||||
{conditions} """.format(conditions=conditions), condition_data_dict)
|
||||
select price_list_rate
|
||||
from `tabItem Price`
|
||||
{conditions}
|
||||
""".format(conditions=conditions),
|
||||
self.as_dict(),)
|
||||
|
||||
if price_list_rate :
|
||||
frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates."), ItemPriceDuplicateItem)
|
||||
if price_list_rate:
|
||||
frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
|
||||
|
||||
def before_save(self):
|
||||
if self.selling:
|
||||
self.reference = self.customer
|
||||
if self.buying:
|
||||
self.reference = self.supplier
|
||||
|
||||
|
||||
if self.selling and not self.buying:
|
||||
# if only selling then remove supplier
|
||||
self.supplier = None
|
||||
|
@ -138,4 +138,23 @@ class TestItemPrice(unittest.TestCase):
|
||||
# Valid price list must already exist
|
||||
self.assertRaises(frappe.ValidationError, doc.save)
|
||||
|
||||
def test_empty_duplicate_validation(self):
|
||||
# Check if none/empty values are not compared during insert validation
|
||||
doc = frappe.copy_doc(test_records[2])
|
||||
doc.customer = None
|
||||
doc.price_list_rate = 21
|
||||
doc.insert()
|
||||
|
||||
args = {
|
||||
"price_list": doc.price_list,
|
||||
"uom": "_Test UOM",
|
||||
"transaction_date": '2017-04-18',
|
||||
"qty": 7
|
||||
}
|
||||
|
||||
price = get_price_list_rate_for(args, doc.item_code)
|
||||
frappe.db.rollback()
|
||||
|
||||
self.assertEqual(price, 21)
|
||||
|
||||
test_records = frappe.get_test_records('Item Price')
|
||||
|
@ -1,88 +1,57 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"beta": 0,
|
||||
"creation": "2013-02-22 01:28:01",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2013-02-22 01:28:01",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"specification",
|
||||
"value",
|
||||
"column_break_3",
|
||||
"acceptance_formula"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "specification",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Parameter",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "specification",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "200px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"fieldname": "specification",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Parameter",
|
||||
"oldfieldname": "specification",
|
||||
"oldfieldtype": "Data",
|
||||
"print_width": "200px",
|
||||
"reqd": 1,
|
||||
"width": "200px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "value",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Acceptance Criteria",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "value",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"fieldname": "value",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Acceptance Criteria",
|
||||
"oldfieldname": "value",
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"description": "Simple Python formula based on numeric Readings.<br> Example 1: <b>reading_1 > 0.2 and reading_1 < 0.5</b><br>\nExample 2: <b>(reading_1 + reading_2) / 2 < 10</b>",
|
||||
"fieldname": "acceptance_formula",
|
||||
"fieldtype": "Code",
|
||||
"in_list_view": 1,
|
||||
"label": "Acceptance Criteria Formula"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-07-11 03:28:01.074316",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item Quality Inspection Parameter",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"track_seen": 0
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-11-16 16:33:42.421842",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item Quality Inspection Parameter",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
@ -1109,7 +1110,7 @@
|
||||
"idx": 261,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-03 23:20:26.381024",
|
||||
"modified": "2020-10-30 14:00:08.347534",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt",
|
||||
|
@ -4,15 +4,20 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \
|
||||
import get_template_details
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
class QualityInspection(Document):
|
||||
def validate(self):
|
||||
if not self.readings and self.item_code:
|
||||
self.get_item_specification_details()
|
||||
|
||||
if self.readings:
|
||||
self.set_status_based_on_acceptance_formula()
|
||||
|
||||
def get_item_specification_details(self):
|
||||
if not self.quality_inspection_template:
|
||||
self.quality_inspection_template = frappe.db.get_value('Item',
|
||||
@ -26,6 +31,7 @@ class QualityInspection(Document):
|
||||
child = self.append('readings', {})
|
||||
child.specification = d.specification
|
||||
child.value = d.value
|
||||
child.acceptance_formula = d.acceptance_formula
|
||||
child.status = "Accepted"
|
||||
|
||||
def get_quality_inspection_template(self):
|
||||
@ -58,6 +64,29 @@ class QualityInspection(Document):
|
||||
.format(parent_doc=self.reference_type, child_doc=doctype),
|
||||
(quality_inspection, self.modified, self.reference_name, self.item_code))
|
||||
|
||||
def set_status_based_on_acceptance_formula(self):
|
||||
for reading in self.readings:
|
||||
if not reading.acceptance_formula: continue
|
||||
|
||||
condition = reading.acceptance_formula
|
||||
data = {}
|
||||
for i in range(1, 11):
|
||||
field = "reading_" + str(i)
|
||||
data[field] = flt(reading.get(field)) or 0
|
||||
|
||||
try:
|
||||
result = frappe.safe_eval(condition, None, data)
|
||||
reading.status = "Accepted" if result else "Rejected"
|
||||
except SyntaxError:
|
||||
frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx),
|
||||
title=_("Invalid Formula"))
|
||||
except NameError as e:
|
||||
field = frappe.bold(e.args[0].split()[1])
|
||||
frappe.throw(_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.")
|
||||
.format(reading.idx, field),
|
||||
title=_("Invalid Formula"))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def item_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
|
@ -7,6 +7,7 @@ import unittest
|
||||
from frappe.utils import nowdate
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError, QualityInspectionNotSubmittedError
|
||||
|
||||
# test_records = frappe.get_test_records('Quality Inspection')
|
||||
@ -17,10 +18,12 @@ class TestQualityInspection(unittest.TestCase):
|
||||
frappe.db.set_value("Item", "_Test Item with QA", "inspection_required_before_delivery", 1)
|
||||
|
||||
def test_qa_for_delivery(self):
|
||||
make_stock_entry(item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100)
|
||||
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
|
||||
|
||||
self.assertRaises(QualityInspectionRequiredError, dn.submit)
|
||||
|
||||
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected", submit=True)
|
||||
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected")
|
||||
dn.reload()
|
||||
self.assertRaises(QualityInspectionRejectedError, dn.submit)
|
||||
|
||||
@ -28,12 +31,51 @@ class TestQualityInspection(unittest.TestCase):
|
||||
dn.reload()
|
||||
dn.submit()
|
||||
|
||||
qa.cancel()
|
||||
dn.reload()
|
||||
dn.cancel()
|
||||
|
||||
def test_qa_not_submit(self):
|
||||
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
|
||||
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, submit = False)
|
||||
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True)
|
||||
dn.items[0].quality_inspection = qa.name
|
||||
self.assertRaises(QualityInspectionNotSubmittedError, dn.submit)
|
||||
|
||||
qa.delete()
|
||||
dn.delete()
|
||||
|
||||
def test_formula_based_qi_readings(self):
|
||||
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
|
||||
readings = [{
|
||||
"specification": "Iron Content",
|
||||
"acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
|
||||
"reading_1": 0.4
|
||||
},
|
||||
{
|
||||
"specification": "Calcium Content",
|
||||
"acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
|
||||
"reading_1": 0.7
|
||||
},
|
||||
{
|
||||
"specification": "Mg Content",
|
||||
"acceptance_formula": "(reading_1 + reading_2 + reading_3) / 3 < 0.9",
|
||||
"reading_1": 0.5,
|
||||
"reading_2": 0.7,
|
||||
"reading_3": "random text" # check if random string input causes issues
|
||||
}]
|
||||
|
||||
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name,
|
||||
readings=readings, do_not_save=True)
|
||||
qa.save()
|
||||
|
||||
# status must be auto set as per formula
|
||||
self.assertEqual(qa.readings[0].status, "Accepted")
|
||||
self.assertEqual(qa.readings[1].status, "Rejected")
|
||||
self.assertEqual(qa.readings[2].status, "Accepted")
|
||||
|
||||
qa.delete()
|
||||
dn.delete()
|
||||
|
||||
def create_quality_inspection(**args):
|
||||
args = frappe._dict(args)
|
||||
qa = frappe.new_doc("Quality Inspection")
|
||||
@ -44,12 +86,18 @@ def create_quality_inspection(**args):
|
||||
qa.item_code = args.item_code or "_Test Item with QA"
|
||||
qa.sample_size = 1
|
||||
qa.inspected_by = frappe.session.user
|
||||
qa.append("readings", {
|
||||
"specification": "Size",
|
||||
"status": args.status
|
||||
})
|
||||
qa.save()
|
||||
if args.submit:
|
||||
qa.submit()
|
||||
|
||||
readings = args.readings or {"specification": "Size", "status": args.status}
|
||||
|
||||
if isinstance(readings, list):
|
||||
for entry in readings:
|
||||
qa.append("readings", entry)
|
||||
else:
|
||||
qa.append("readings", readings)
|
||||
|
||||
if not args.do_not_save:
|
||||
qa.save()
|
||||
if not args.do_not_submit:
|
||||
qa.submit()
|
||||
|
||||
return qa
|
||||
|
@ -1,22 +1,29 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2013-02-22 01:27:43",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"specification",
|
||||
"value",
|
||||
"status",
|
||||
"column_break_4",
|
||||
"acceptance_formula",
|
||||
"section_break_3",
|
||||
"reading_1",
|
||||
"reading_2",
|
||||
"reading_3",
|
||||
"column_break_10",
|
||||
"reading_4",
|
||||
"reading_5",
|
||||
"reading_6",
|
||||
"column_break_14",
|
||||
"reading_7",
|
||||
"reading_8",
|
||||
"reading_9",
|
||||
"reading_10",
|
||||
"status"
|
||||
"reading_10"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -124,15 +131,40 @@
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Accepted\nRejected"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_3",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"description": "Simple Python formula based on numeric Readings.<br> Example 1: <b>reading_1 > 0.2 and reading_1 < 0.5</b><br>\nExample 2: <b>(reading_1 + reading_2) / 2 < 10</b>",
|
||||
"fieldname": "acceptance_formula",
|
||||
"fieldtype": "Code",
|
||||
"label": "Acceptance Criteria Formula"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_14",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2019-07-11 18:48:12.667404",
|
||||
"links": [],
|
||||
"modified": "2020-11-16 16:34:29.947856",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Quality Inspection Reading",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -12,5 +12,7 @@ class QualityInspectionTemplate(Document):
|
||||
def get_template_details(template):
|
||||
if not template: return []
|
||||
|
||||
return frappe.get_all('Item Quality Inspection Parameter', fields=["specification", "value"],
|
||||
filters={'parenttype': 'Quality Inspection Template', 'parent': template}, order_by="idx")
|
||||
return frappe.get_all('Item Quality Inspection Parameter',
|
||||
fields=["specification", "value", "acceptance_formula"],
|
||||
filters={'parenttype': 'Quality Inspection Template', 'parent': template},
|
||||
order_by="idx")
|
@ -20,7 +20,8 @@ def execute(filters=None):
|
||||
|
||||
fifo_queue = sorted(filter(_func, item_dict["fifo_queue"]), key=_func)
|
||||
details = item_dict["details"]
|
||||
if not fifo_queue and (not item_dict.get("total_qty")): continue
|
||||
|
||||
if not fifo_queue: continue
|
||||
|
||||
average_age = get_average_age(fifo_queue, to_date)
|
||||
earliest_age = date_diff(to_date, fifo_queue[0][1])
|
||||
|
Loading…
x
Reference in New Issue
Block a user