Merge branch 'develop' into github-issue-33344
diff --git a/README.md b/README.md
index 0708266..44bd729 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@
1. [Frappe School](https://frappe.school) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
2. [Official documentation](https://docs.erpnext.com/) - Extensive documentation for ERPNext.
3. [Discussion Forum](https://discuss.erpnext.com/) - Engage with community of ERPNext users and service providers.
-4. [Telegram Group](https://t.me/erpnexthelp) - Get instant help from huge community of users.
+4. [Telegram Group](https://erpnext_public.t.me) - Get instant help from huge community of users.
## Contributing
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
index 75f8f06..9e67c4c 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
@@ -29,6 +29,7 @@
"root_type",
"is_group",
"tax_rate",
+ "account_currency",
]:
account_number = cstr(child.get("account_number")).strip()
@@ -95,7 +96,17 @@
is_group = child.get("is_group")
elif len(
set(child.keys())
- - set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])
+ - set(
+ [
+ "account_name",
+ "account_type",
+ "root_type",
+ "is_group",
+ "tax_rate",
+ "account_number",
+ "account_currency",
+ ]
+ )
):
is_group = 1
else:
@@ -185,6 +196,7 @@
"root_type",
"tax_rate",
"account_number",
+ "account_currency",
],
order_by="lft, rgt",
)
@@ -267,6 +279,7 @@
"root_type",
"is_group",
"tax_rate",
+ "account_currency",
]:
continue
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index 220b747..cb7da17 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -36,7 +36,7 @@
no_of_columns = max([len(d) for d in data])
- if no_of_columns > 7:
+ if no_of_columns > 8:
frappe.throw(
_("More columns found than expected. Please compare the uploaded file with standard template"),
title=(_("Wrong Template")),
@@ -233,6 +233,7 @@
is_group,
account_type,
root_type,
+ account_currency,
) = i
if not account_name:
@@ -253,6 +254,8 @@
charts_map[account_name]["account_type"] = account_type
if root_type:
charts_map[account_name]["root_type"] = root_type
+ if account_currency:
+ charts_map[account_name]["account_currency"] = account_currency
path = return_parent(data, account_name)[::-1]
paths.append(path) # List of path is created
line_no += 1
@@ -315,6 +318,7 @@
"Is Group",
"Account Type",
"Root Type",
+ "Account Currency",
]
writer = UnicodeWriter()
writer.writerow(fields)
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index 655c4ec..115b415 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -21,8 +21,24 @@
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
+ self.validate_duplicate_pos_invoices()
self.validate_pos_invoices()
+ def validate_duplicate_pos_invoices(self):
+ pos_occurences = {}
+ for idx, inv in enumerate(self.pos_transactions, 1):
+ pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
+
+ error_list = []
+ for key, value in pos_occurences.items():
+ if len(value) > 1:
+ error_list.append(
+ _("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
+ )
+
+ if error_list:
+ frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
+
def validate_pos_invoices(self):
invalid_rows = []
for d in self.pos_transactions:
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index 3a237a4..b1e2208 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -17,6 +17,22 @@
def validate(self):
self.validate_customer()
self.validate_pos_invoice_status()
+ self.validate_duplicate_pos_invoices()
+
+ def validate_duplicate_pos_invoices(self):
+ pos_occurences = {}
+ for idx, inv in enumerate(self.pos_invoices, 1):
+ pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
+
+ error_list = []
+ for key, value in pos_occurences.items():
+ if len(value) > 1:
+ error_list.append(
+ _("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
+ )
+
+ if error_list:
+ frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
def validate_customer(self):
if self.merge_invoices_based_on == "Customer Group":
@@ -425,6 +441,8 @@
if closing_entry:
closing_entry.set_status(update=True, status="Failed")
+ if type(error_message) == list:
+ error_message = frappe.json.dumps(error_message)
closing_entry.db_set("error_message", error_message)
raise
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index f588d5c..21addab 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -5,6 +5,7 @@
import frappe
from frappe import _, throw
from frappe.model.mapper import get_mapped_doc
+from frappe.query_builder.functions import Sum
from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
import erpnext
@@ -1465,19 +1466,16 @@
def update_billing_status_in_pr(self, update_modified=True):
updated_pr = []
po_details = []
+
+ pr_details_billed_amt = self.get_pr_details_billed_amt()
+
for d in self.get("items"):
if d.pr_detail:
- billed_amt = frappe.db.sql(
- """select sum(amount) from `tabPurchase Invoice Item`
- where pr_detail=%s and docstatus=1""",
- d.pr_detail,
- )
- billed_amt = billed_amt and billed_amt[0][0] or 0
frappe.db.set_value(
"Purchase Receipt Item",
d.pr_detail,
"billed_amt",
- billed_amt,
+ flt(pr_details_billed_amt.get(d.pr_detail)),
update_modified=update_modified,
)
updated_pr.append(d.purchase_receipt)
@@ -1493,6 +1491,24 @@
pr_doc = frappe.get_doc("Purchase Receipt", pr)
update_billing_percentage(pr_doc, update_modified=update_modified)
+ def get_pr_details_billed_amt(self):
+ # Get billed amount based on purchase receipt item reference (pr_detail) in purchase invoice
+
+ pr_details_billed_amt = {}
+ pr_details = [d.get("pr_detail") for d in self.get("items") if d.get("pr_detail")]
+ if pr_details:
+ doctype = frappe.qb.DocType("Purchase Invoice Item")
+ query = (
+ frappe.qb.from_(doctype)
+ .select(doctype.pr_detail, Sum(doctype.amount))
+ .where(doctype.pr_detail.isin(pr_details) & doctype.docstatus == 1)
+ .groupby(doctype.pr_detail)
+ )
+
+ pr_details_billed_amt = frappe._dict(query.run(as_list=1))
+
+ return pr_details_billed_amt
+
def on_recurring(self, reference_doc, auto_repeat_doc):
self.due_date = None
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 2c829b2..f0146ea 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -278,7 +278,7 @@
tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers)
if cint(tax_details.round_off_tax_amount):
- tax_amount = round(tax_amount)
+ tax_amount = normal_round(tax_amount)
return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount
@@ -603,3 +603,20 @@
valid = True
return valid
+
+
+def normal_round(number):
+ """
+ Rounds a number to the nearest integer.
+ :param number: The number to round.
+ """
+ decimal_part = number - int(number)
+
+ if decimal_part >= 0.5:
+ decimal_part = 1
+ else:
+ decimal_part = 0
+
+ number = int(number) + decimal_part
+
+ return number
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index e23265b..fde4de8 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -395,6 +395,7 @@
class GrossProfitGenerator(object):
def __init__(self, filters=None):
+ self.sle = {}
self.data = []
self.average_buying_rate = {}
self.filters = frappe._dict(filters)
@@ -404,7 +405,6 @@
if filters.group_by == "Invoice":
self.group_items_by_invoice()
- self.load_stock_ledger_entries()
self.load_product_bundle()
self.load_non_stock_items()
self.get_returned_invoice_items()
@@ -633,7 +633,7 @@
return flt(row.qty) * item_rate
else:
- my_sle = self.sle.get((item_code, row.warehouse))
+ my_sle = self.get_stock_ledger_entries(item_code, row.warehouse)
if (row.update_stock or row.dn_detail) and my_sle:
parenttype, parent = row.parenttype, row.parent
if row.dn_detail:
@@ -651,7 +651,7 @@
dn["item_row"],
dn["warehouse"],
)
- my_sle = self.sle.get((item_code, warehouse))
+ my_sle = self.get_stock_ledger_entries(item_code, row.warehouse)
return self.calculate_buying_amount_from_sle(
row, my_sle, parenttype, parent, item_row, item_code
)
@@ -667,15 +667,12 @@
def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code):
from frappe.query_builder.functions import Sum
- delivery_note = frappe.qb.DocType("Delivery Note")
delivery_note_item = frappe.qb.DocType("Delivery Note Item")
query = (
- frappe.qb.from_(delivery_note)
- .inner_join(delivery_note_item)
- .on(delivery_note.name == delivery_note_item.parent)
+ frappe.qb.from_(delivery_note_item)
.select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty))
- .where(delivery_note.docstatus == 1)
+ .where(delivery_note_item.docstatus == 1)
.where(delivery_note_item.item_code == item_code)
.where(delivery_note_item.against_sales_order == sales_order)
.where(delivery_note_item.so_detail == so_detail)
@@ -947,24 +944,36 @@
"Item", item_code, ["item_name", "description", "item_group", "brand"]
)
- def load_stock_ledger_entries(self):
- res = frappe.db.sql(
- """select item_code, voucher_type, voucher_no,
- voucher_detail_no, stock_value, warehouse, actual_qty as qty
- from `tabStock Ledger Entry`
- where company=%(company)s and is_cancelled = 0
- order by
- item_code desc, warehouse desc, posting_date desc,
- posting_time desc, creation desc""",
- self.filters,
- as_dict=True,
- )
- self.sle = {}
- for r in res:
- if (r.item_code, r.warehouse) not in self.sle:
- self.sle[(r.item_code, r.warehouse)] = []
+ def get_stock_ledger_entries(self, item_code, warehouse):
+ if item_code and warehouse:
+ if (item_code, warehouse) not in self.sle:
+ sle = qb.DocType("Stock Ledger Entry")
+ res = (
+ qb.from_(sle)
+ .select(
+ sle.item_code,
+ sle.voucher_type,
+ sle.voucher_no,
+ sle.voucher_detail_no,
+ sle.stock_value,
+ sle.warehouse,
+ sle.actual_qty.as_("qty"),
+ )
+ .where(
+ (sle.company == self.filters.company)
+ & (sle.item_code == item_code)
+ & (sle.warehouse == warehouse)
+ & (sle.is_cancelled == 0)
+ )
+ .orderby(sle.item_code)
+ .orderby(sle.warehouse, sle.posting_date, sle.posting_time, sle.creation, order=Order.desc)
+ .run(as_dict=True)
+ )
- self.sle[(r.item_code, r.warehouse)].append(r)
+ self.sle[(item_code, warehouse)] = res
+
+ return self.sle[(item_code, warehouse)]
+ return []
def load_product_bundle(self):
self.product_bundles = {}
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js
index c28b2b3..3d2dff1 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js
@@ -43,9 +43,9 @@
if(frm.doc.depreciation_method != "Manual") return;
var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation);
- $.each(frm.doc.schedules || [], function(i, row) {
+
+ $.each(frm.doc.depreciation_schedule || [], function(i, row) {
accumulated_depreciation += flt(row.depreciation_amount);
- frappe.model.set_value(row.doctype, row.name,
- "accumulated_depreciation_amount", accumulated_depreciation);
+ frappe.model.set_value(row.doctype, row.name, "accumulated_depreciation_amount", accumulated_depreciation);
})
};
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
index 898c482..d38508d 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
@@ -10,7 +10,9 @@
"asset",
"naming_series",
"column_break_2",
+ "gross_purchase_amount",
"opening_accumulated_depreciation",
+ "number_of_depreciations_booked",
"finance_book",
"finance_book_id",
"depreciation_details_section",
@@ -148,18 +150,36 @@
"read_only": 1
},
{
- "depends_on": "opening_accumulated_depreciation",
"fieldname": "opening_accumulated_depreciation",
"fieldtype": "Currency",
+ "hidden": 1,
"label": "Opening Accumulated Depreciation",
"options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "gross_purchase_amount",
+ "fieldtype": "Currency",
+ "hidden": 1,
+ "label": "Gross Purchase Amount",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "number_of_depreciations_booked",
+ "fieldtype": "Int",
+ "hidden": 1,
+ "label": "Number of Depreciations Booked",
+ "print_hide": 1,
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-01-16 21:08:21.421260",
+ "modified": "2023-02-26 16:37:23.734806",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Depreciation Schedule",
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
index 6f02662..b75fbcb 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
@@ -4,7 +4,15 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import add_days, add_months, cint, flt, get_last_day, is_last_day_of_the_month
+from frappe.utils import (
+ add_days,
+ add_months,
+ cint,
+ flt,
+ get_last_day,
+ getdate,
+ is_last_day_of_the_month,
+)
class AssetDepreciationSchedule(Document):
@@ -83,15 +91,58 @@
date_of_return=None,
update_asset_finance_book_row=True,
):
+ have_asset_details_been_modified = self.have_asset_details_been_modified(asset_doc)
+ not_manual_depr_or_have_manual_depr_details_been_modified = (
+ self.not_manual_depr_or_have_manual_depr_details_been_modified(row)
+ )
+
self.set_draft_asset_depr_schedule_details(asset_doc, row)
- self.make_depr_schedule(asset_doc, row, date_of_disposal, update_asset_finance_book_row)
- self.set_accumulated_depreciation(row, date_of_disposal, date_of_return)
+
+ if self.should_prepare_depreciation_schedule(
+ have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified
+ ):
+ self.make_depr_schedule(asset_doc, row, date_of_disposal, update_asset_finance_book_row)
+ self.set_accumulated_depreciation(row, date_of_disposal, date_of_return)
+
+ def have_asset_details_been_modified(self, asset_doc):
+ return (
+ asset_doc.gross_purchase_amount != self.gross_purchase_amount
+ or asset_doc.opening_accumulated_depreciation != self.opening_accumulated_depreciation
+ or asset_doc.number_of_depreciations_booked != self.number_of_depreciations_booked
+ )
+
+ def not_manual_depr_or_have_manual_depr_details_been_modified(self, row):
+ return (
+ self.depreciation_method != "Manual"
+ or row.total_number_of_depreciations != self.total_number_of_depreciations
+ or row.frequency_of_depreciation != self.frequency_of_depreciation
+ or getdate(row.depreciation_start_date) != self.get("depreciation_schedule")[0].schedule_date
+ or row.expected_value_after_useful_life != self.expected_value_after_useful_life
+ )
+
+ def should_prepare_depreciation_schedule(
+ self, have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified
+ ):
+ if not self.get("depreciation_schedule"):
+ return True
+
+ old_asset_depr_schedule_doc = self.get_doc_before_save()
+
+ if self.docstatus != 0 and not old_asset_depr_schedule_doc:
+ return True
+
+ if have_asset_details_been_modified or not_manual_depr_or_have_manual_depr_details_been_modified:
+ return True
+
+ return False
def set_draft_asset_depr_schedule_details(self, asset_doc, row):
self.asset = asset_doc.name
self.finance_book = row.finance_book
self.finance_book_id = row.idx
self.opening_accumulated_depreciation = asset_doc.opening_accumulated_depreciation
+ self.number_of_depreciations_booked = asset_doc.number_of_depreciations_booked
+ self.gross_purchase_amount = asset_doc.gross_purchase_amount
self.depreciation_method = row.depreciation_method
self.total_number_of_depreciations = row.total_number_of_depreciations
self.frequency_of_depreciation = row.frequency_of_depreciation
@@ -102,7 +153,7 @@
def make_depr_schedule(
self, asset_doc, row, date_of_disposal, update_asset_finance_book_row=True
):
- if row.depreciation_method != "Manual" and not self.get("depreciation_schedule"):
+ if not self.get("depreciation_schedule"):
self.depreciation_schedule = []
if not asset_doc.available_for_use_date:
@@ -293,7 +344,9 @@
ignore_booked_entry=False,
):
straight_line_idx = [
- d.idx for d in self.get("depreciation_schedule") if d.depreciation_method == "Straight Line"
+ d.idx
+ for d in self.get("depreciation_schedule")
+ if d.depreciation_method == "Straight Line" or d.depreciation_method == "Manual"
]
accumulated_depreciation = flt(self.opening_accumulated_depreciation)
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
index 51a6a86..59d43b1 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -151,6 +151,7 @@
filters.filter_based_on,
"Monthly",
company=filters.company,
+ ignore_fiscal_year=True,
)
for d in period_list:
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index fc6793a..9fcb769 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -252,7 +252,6 @@
child.parent = par.name and par.docstatus = 1
and par.is_return = 1 and par.return_against = %s
group by item_code
- for update
""".format(
column, doc.doctype, doc.doctype
),
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index fd19d25..fba886c 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -311,15 +311,10 @@
"on_submit": [
"erpnext.regional.create_transaction_log",
"erpnext.regional.italy.utils.sales_invoice_on_submit",
- "erpnext.regional.saudi_arabia.utils.create_qr_code",
],
- "on_cancel": [
- "erpnext.regional.italy.utils.sales_invoice_on_cancel",
- "erpnext.regional.saudi_arabia.utils.delete_qr_code_file",
- ],
+ "on_cancel": ["erpnext.regional.italy.utils.sales_invoice_on_cancel"],
"on_trash": "erpnext.regional.check_deletion_permission",
},
- "POS Invoice": {"on_submit": ["erpnext.regional.saudi_arabia.utils.create_qr_code"]},
"Purchase Invoice": {
"validate": [
"erpnext.regional.united_arab_emirates.utils.update_grand_total_for_rcm",
@@ -347,7 +342,6 @@
"Email Unsubscribe": {
"after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"
},
- "Company": {"on_trash": ["erpnext.regional.saudi_arabia.utils.delete_vat_settings_for_company"]},
"Integration Request": {
"validate": "erpnext.accounts.doctype.payment_request.payment_request.validate_payment"
},
diff --git a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json
index 158f143..ba05355 100644
--- a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json
+++ b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json
@@ -64,8 +64,6 @@
"fieldtype": "Section Break"
},
{
- "fetch_from": "prevdoc_detail_docname.sales_person",
- "fetch_if_empty": 1,
"fieldname": "service_person",
"fieldtype": "Link",
"in_list_view": 1,
@@ -110,13 +108,15 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-05-27 17:47:21.474282",
+ "modified": "2023-02-27 11:09:33.114458",
"modified_by": "Administrator",
"module": "Maintenance",
"name": "Maintenance Visit Purpose",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
index 7beecac..e7f67ca 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
@@ -25,8 +25,9 @@
],
"formatter": function(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
+
if (column.id == "item") {
- if (data["enough_parts_to_build"] > 0) {
+ if (data["in_stock_qty"] >= data["required_qty"]) {
value = `<a style='color:green' href="/app/item/${data['item']}" data-doctype="Item">${data['item']}</a>`;
} else {
value = `<a style='color:red' href="/app/item/${data['item']}" data-doctype="Item">${data['item']}</a>`;
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index e6be933..2abd65b 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -250,18 +250,14 @@
erpnext.patches.v13_0.update_category_in_ltds_certificate
erpnext.patches.v13_0.fetch_thumbnail_in_website_items
erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit
-erpnext.patches.v13_0.create_ksa_vat_custom_fields # 07-01-2022
erpnext.patches.v14_0.migrate_crm_settings
-erpnext.patches.v13_0.rename_ksa_qr_field
erpnext.patches.v13_0.wipe_serial_no_field_for_0_qty
-erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021
erpnext.patches.v13_0.agriculture_deprecation_warning
erpnext.patches.v13_0.hospitality_deprecation_warning
erpnext.patches.v13_0.update_asset_quantity_field
erpnext.patches.v13_0.delete_bank_reconciliation_detail
erpnext.patches.v13_0.enable_provisional_accounting
erpnext.patches.v13_0.non_profit_deprecation_warning
-erpnext.patches.v13_0.enable_ksa_vat_docs #1
erpnext.patches.v13_0.show_india_localisation_deprecation_warning
erpnext.patches.v13_0.show_hr_payroll_deprecation_warning
erpnext.patches.v13_0.reset_corrupt_defaults
@@ -269,6 +265,8 @@
erpnext.patches.v15_0.delete_taxjar_doctypes
erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets
erpnext.patches.v14_0.update_reference_due_date_in_journal_entry
+erpnext.patches.v15_0.saudi_depreciation_warning
+erpnext.patches.v15_0.delete_saudi_doctypes
[post_model_sync]
execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
diff --git a/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py b/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py
deleted file mode 100644
index 093463a..0000000
--- a/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import frappe
-
-from erpnext.regional.saudi_arabia.setup import make_custom_fields
-
-
-def execute():
- company = frappe.get_all("Company", filters={"country": "Saudi Arabia"})
- if not company:
- return
-
- make_custom_fields()
diff --git a/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py b/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py
deleted file mode 100644
index 84b6c37..0000000
--- a/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2020, Wahni Green Technologies and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-from erpnext.regional.saudi_arabia.setup import add_print_formats
-
-
-def execute():
- company = frappe.get_all("Company", filters={"country": "Saudi Arabia"})
- if company:
- add_print_formats()
- return
-
- if frappe.db.exists("DocType", "Print Format"):
- frappe.reload_doc("regional", "print_format", "ksa_vat_invoice", force=True)
- frappe.reload_doc("regional", "print_format", "ksa_pos_invoice", force=True)
- for d in ("KSA VAT Invoice", "KSA POS Invoice"):
- frappe.db.set_value("Print Format", d, "disabled", 1)
diff --git a/erpnext/patches/v13_0/enable_ksa_vat_docs.py b/erpnext/patches/v13_0/enable_ksa_vat_docs.py
deleted file mode 100644
index 4adf4d7..0000000
--- a/erpnext/patches/v13_0/enable_ksa_vat_docs.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import frappe
-
-from erpnext.regional.saudi_arabia.setup import add_permissions, add_print_formats
-
-
-def execute():
- company = frappe.get_all("Company", filters={"country": "Saudi Arabia"})
- if not company:
- return
-
- add_print_formats()
- add_permissions()
diff --git a/erpnext/patches/v13_0/rename_ksa_qr_field.py b/erpnext/patches/v13_0/rename_ksa_qr_field.py
deleted file mode 100644
index e4b9141..0000000
--- a/erpnext/patches/v13_0/rename_ksa_qr_field.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (c) 2020, Wahni Green Technologies and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-from frappe.model.utils.rename_field import rename_field
-
-
-def execute():
- company = frappe.get_all("Company", filters={"country": "Saudi Arabia"})
- if not company:
- return
-
- if frappe.db.exists("DocType", "Sales Invoice"):
- frappe.reload_doc("accounts", "doctype", "sales_invoice", force=True)
-
- # rename_field method assumes that the field already exists or the doc is synced
- if not frappe.db.has_column("Sales Invoice", "ksa_einv_qr"):
- create_custom_fields(
- {
- "Sales Invoice": [
- dict(
- fieldname="ksa_einv_qr",
- label="KSA E-Invoicing QR",
- fieldtype="Attach Image",
- read_only=1,
- no_copy=1,
- hidden=1,
- )
- ]
- }
- )
-
- if frappe.db.has_column("Sales Invoice", "qr_code"):
- rename_field("Sales Invoice", "qr_code", "ksa_einv_qr")
- frappe.delete_doc_if_exists("Custom Field", "Sales Invoice-qr_code")
diff --git a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
index 371ecbc..5c46bf3 100644
--- a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
+++ b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
@@ -27,7 +27,13 @@
records = (
frappe.qb.from_(asset)
- .select(asset.name, asset.opening_accumulated_depreciation, asset.docstatus)
+ .select(
+ asset.name,
+ asset.opening_accumulated_depreciation,
+ asset.gross_purchase_amount,
+ asset.number_of_depreciations_booked,
+ asset.docstatus,
+ )
.where(asset.calculate_depreciation == 1)
.where(asset.docstatus < 2)
).run(as_dict=True)
diff --git a/erpnext/patches/v15_0/delete_saudi_doctypes.py b/erpnext/patches/v15_0/delete_saudi_doctypes.py
new file mode 100644
index 0000000..371e335
--- /dev/null
+++ b/erpnext/patches/v15_0/delete_saudi_doctypes.py
@@ -0,0 +1,25 @@
+import click
+import frappe
+
+
+def execute():
+ if "ksa" in frappe.get_installed_apps():
+ return
+
+ doctypes = ["KSA VAT Setting", "KSA VAT Purchase Account", "KSA VAT Sales Account"]
+ for doctype in doctypes:
+ frappe.delete_doc("DocType", doctype, ignore_missing=True)
+
+ print_formats = ["KSA POS Invoice", "KSA VAT Invoice"]
+ for print_format in print_formats:
+ frappe.delete_doc("Print Format", print_format, ignore_missing=True, force=True)
+
+ reports = ["KSA VAT"]
+ for report in reports:
+ frappe.delete_doc("Report", report, ignore_missing=True, force=True)
+
+ click.secho(
+ "Region Saudi Arabia(KSA) is moved to a separate app"
+ "Please install the app to continue using the module: https://github.com/8848digital/KSA",
+ fg="yellow",
+ )
diff --git a/erpnext/patches/v15_0/saudi_depreciation_warning.py b/erpnext/patches/v15_0/saudi_depreciation_warning.py
new file mode 100644
index 0000000..6af8efe
--- /dev/null
+++ b/erpnext/patches/v15_0/saudi_depreciation_warning.py
@@ -0,0 +1,12 @@
+import click
+import frappe
+
+
+def execute():
+ if "ksa" in frappe.get_installed_apps():
+ return
+ click.secho(
+ "Region Saudi Arabia(KSA) is moved to a separate app\n"
+ "Please install the app to continue using the KSA Features: https://github.com/8848digital/KSA",
+ fg="yellow",
+ )
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 51dcd64..58aa8d7 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -221,9 +221,9 @@
callback: function(r) {
if (r.message && r.message.length) {
r.message.forEach((dimension) => {
- let found = filters.some(el => el.fieldname === dimension['fieldname']);
+ let existing_filter = filters.filter(el => el.fieldname === dimension['fieldname']);
- if (!found) {
+ if (!existing_filter.length) {
filters.splice(index, 0, {
"fieldname": dimension["fieldname"],
"label": __(dimension["doctype"]),
@@ -232,6 +232,11 @@
return frappe.db.get_link_options(dimension["doctype"], txt);
},
});
+ } else {
+ existing_filter[0]['fieldtype'] = "MultiSelectList";
+ existing_filter[0]['get_data'] = function(txt) {
+ return frappe.db.get_link_options(dimension["doctype"], txt);
+ }
}
});
}
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py b/erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json
deleted file mode 100644
index 89ba3e9..0000000
--- a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "actions": [],
- "creation": "2021-07-13 09:17:09.862163",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "title",
- "item_tax_template",
- "account"
- ],
- "fields": [
- {
- "fieldname": "account",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Account",
- "options": "Account",
- "reqd": 1
- },
- {
- "fieldname": "title",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Title",
- "reqd": 1
- },
- {
- "fieldname": "item_tax_template",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Item Tax Template",
- "options": "Item Tax Template",
- "reqd": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-08-04 06:42:38.205597",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "KSA VAT Purchase Account",
- "owner": "Administrator",
- "permissions": [],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py
deleted file mode 100644
index 3920bc5..0000000
--- a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2021, Havenir Solutions and contributors
-# For license information, please see license.txt
-
-# import frappe
-from frappe.model.document import Document
-
-
-class KSAVATPurchaseAccount(Document):
- pass
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/__init__.py b/erpnext/regional/doctype/ksa_vat_sales_account/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/doctype/ksa_vat_sales_account/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js
deleted file mode 100644
index 72613f4..0000000
--- a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2021, Havenir Solutions and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('KSA VAT Sales Account', {
- // refresh: function(frm) {
-
- // }
-});
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json
deleted file mode 100644
index df27478..0000000
--- a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "actions": [],
- "creation": "2021-07-13 08:46:33.820968",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "title",
- "item_tax_template",
- "account"
- ],
- "fields": [
- {
- "fieldname": "account",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Account",
- "options": "Account",
- "reqd": 1
- },
- {
- "fieldname": "title",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Title",
- "reqd": 1
- },
- {
- "fieldname": "item_tax_template",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Item Tax Template",
- "options": "Item Tax Template",
- "reqd": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-08-04 06:42:00.081407",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "KSA VAT Sales Account",
- "owner": "Administrator",
- "permissions": [],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py
deleted file mode 100644
index 7c2689f..0000000
--- a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2021, Havenir Solutions and contributors
-# For license information, please see license.txt
-
-# import frappe
-from frappe.model.document import Document
-
-
-class KSAVATSalesAccount(Document):
- pass
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py b/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py
deleted file mode 100644
index 1d6a6a7..0000000
--- a/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2021, Havenir Solutions and Contributors
-# See license.txt
-
-# import frappe
-import unittest
-
-
-class TestKSAVATSalesAccount(unittest.TestCase):
- pass
diff --git a/erpnext/regional/doctype/ksa_vat_setting/__init__.py b/erpnext/regional/doctype/ksa_vat_setting/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/doctype/ksa_vat_setting/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
deleted file mode 100644
index 00b62b9..0000000
--- a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2021, Havenir Solutions and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('KSA VAT Setting', {
- onload: function () {
- frappe.breadcrumbs.add('Accounts', 'KSA VAT Setting');
- }
-});
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
deleted file mode 100644
index 3361946..0000000
--- a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "actions": [],
- "autoname": "field:company",
- "creation": "2021-07-13 08:49:01.100356",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "company",
- "ksa_vat_sales_accounts",
- "ksa_vat_purchase_accounts"
- ],
- "fields": [
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Company",
- "options": "Company",
- "reqd": 1,
- "unique": 1
- },
- {
- "fieldname": "ksa_vat_sales_accounts",
- "fieldtype": "Table",
- "label": "KSA VAT Sales Accounts",
- "options": "KSA VAT Sales Account",
- "reqd": 1
- },
- {
- "fieldname": "ksa_vat_purchase_accounts",
- "fieldtype": "Table",
- "label": "KSA VAT Purchase Accounts",
- "options": "KSA VAT Purchase Account",
- "reqd": 1
- }
- ],
- "links": [],
- "modified": "2021-08-26 04:29:06.499378",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "KSA VAT Setting",
- "owner": "Administrator",
- "permissions": [],
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "company",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
deleted file mode 100644
index bdae116..0000000
--- a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2021, Havenir Solutions and contributors
-# For license information, please see license.txt
-
-# import frappe
-from frappe.model.document import Document
-
-
-class KSAVATSetting(Document):
- pass
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
deleted file mode 100644
index 269cbec..0000000
--- a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
+++ /dev/null
@@ -1,5 +0,0 @@
-frappe.listview_settings['KSA VAT Setting'] = {
- onload () {
- frappe.breadcrumbs.add('Accounts');
- }
-}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
deleted file mode 100644
index 7207901..0000000
--- a/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2021, Havenir Solutions and Contributors
-# See license.txt
-
-# import frappe
-import unittest
-
-
-class TestKSAVATSetting(unittest.TestCase):
- pass
diff --git a/erpnext/regional/print_format/ksa_pos_invoice/__init__.py b/erpnext/regional/print_format/ksa_pos_invoice/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/print_format/ksa_pos_invoice/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/print_format/ksa_pos_invoice/ksa_pos_invoice.json b/erpnext/regional/print_format/ksa_pos_invoice/ksa_pos_invoice.json
deleted file mode 100644
index c2a3092..0000000
--- a/erpnext/regional/print_format/ksa_pos_invoice/ksa_pos_invoice.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "absolute_value": 0,
- "align_labels_right": 0,
- "creation": "2021-12-07 13:25:05.424827",
- "css": "",
- "custom_format": 1,
- "default_print_language": "en",
- "disabled": 1,
- "doc_type": "POS Invoice",
- "docstatus": 0,
- "doctype": "Print Format",
- "font_size": 0,
- "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tline-height: 150%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n<p class=\"text-center\" style=\"margin-bottom: 1rem\">\n\t{{ doc.company }}<br>\n\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n\t<img src={{doc.ksa_einv_qr}}>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Cashier\") }}:</b> {{ doc.owner }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Time\") }}:</b> {{ doc.get_formatted(\"posting_time\") }}<br>\n</p>\n\n<hr>\n<table class=\"table table-condensed\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"40%\">{{ _(\"Item\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"35%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"SR.No\") }}:</b><br>\n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"net_amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t<b>{{ _(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.change_amount -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t\t<b>{{ _(\"Change Amount\") }}</b>\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t{%- endif -%}\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
- "idx": 0,
- "line_breaks": 0,
- "margin_bottom": 0.0,
- "margin_left": 0.0,
- "margin_right": 0.0,
- "margin_top": 0.0,
- "modified": "2021-12-08 10:25:01.930885",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "KSA POS Invoice",
- "owner": "Administrator",
- "page_number": "Hide",
- "print_format_builder": 0,
- "print_format_builder_beta": 0,
- "print_format_type": "Jinja",
- "raw_printing": 0,
- "show_section_headings": 0,
- "standard": "Yes"
-}
\ No newline at end of file
diff --git a/erpnext/regional/print_format/ksa_vat_invoice/__init__.py b/erpnext/regional/print_format/ksa_vat_invoice/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/print_format/ksa_vat_invoice/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json b/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json
deleted file mode 100644
index 6b64d47..0000000
--- a/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "absolute_value": 0,
- "align_labels_right": 0,
- "creation": "2021-10-29 22:46:26.039023",
- "css": ".qr-code{\n float:right;\n}\n\n.invoice-heading {\n margin: 0;\n}\n\n.ksa-invoice-table {\n border: 1px solid #888a8e;\n border-collapse: collapse;\n width: 100%;\n margin: 20px 0;\n font-size: 16px;\n}\n\n.ksa-invoice-table.two-columns td:nth-child(2) {\n direction: rtl;\n}\n\n.ksa-invoice-table th {\n border: 1px solid #888a8e;\n max-width: 50%;\n padding: 8px;\n}\n\n.ksa-invoice-table td {\n padding: 5px;\n border: 1px solid #888a8e;\n max-width: 50%;\n}\n\n.ksa-invoice-table thead,\n.ksa-invoice-table tfoot {\n text-transform: uppercase;\n}\n\n.qr-rtl {\n direction: rtl;\n}\n\n.qr-flex{\n display: flex;\n justify-content: space-between;\n}",
- "custom_format": 1,
- "default_print_language": "en",
- "disabled": 1,
- "doc_type": "Sales Invoice",
- "docstatus": 0,
- "doctype": "Print Format",
- "font_size": 14,
- "html": "<div class=\"ksa-vat-format\">\n <div class=\"qr-flex\">\n <div style=\"qr-flex: 1\">\n <h2 class=\"invoice-heading\">TAX INVOICE</h2>\n <h2 class=\"invoice-heading\">\u0641\u0627\u062a\u0648\u0631\u0629 \u0636\u0631\u064a\u0628\u064a\u0629</h2>\n </div>\n \n <img class=\"qr-code\" src={{doc.ksa_einv_qr}}>\n </div>\n {% set company = frappe.get_doc(\"Company\", doc.company)%}\n {% if (doc.company_address) %}\n {% set supplier_address_doc = frappe.get_doc('Address', doc.company_address) %}\n {% endif %}\n \n {% if(doc.customer_address) %}\n {% set customer_address = frappe.get_doc('Address', doc.customer_address ) %}\n {% endif %}\n \n {% if(doc.shipping_address_name) %}\n {% set customer_shipping_address = frappe.get_doc('Address', doc.shipping_address_name ) %}\n {% endif %} \n \n <table class=\"ksa-invoice-table two-columns\">\n <thead>\n <tr>\n <th>{{ company.name }}</th>\n <th style=\"text-align: right;\">{{ company.company_name_in_arabic }}</th>\n </tr>\n </thead>\n\n <tbody>\n <!-- Invoice Info -->\n <tr>\n <td>Invoice#: {{doc.name}}</td>\n <td>\u0631\u0642\u0645 \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.name}}</td>\n </tr>\n <tr>\n <td>Invoice Date: {{doc.posting_date}}</td>\n <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.posting_date}}</td>\n </tr>\n <tr>\n <td>Date of Supply:{{doc.posting_date}}</td>\n <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0648\u0631\u064a\u062f: {{doc.posting_date}}</td>\n </tr>\n \n <!--Supplier Info -->\n <tr>\n <td>Supplier:</td>\n <td>\u0627\u0644\u0645\u0648\u0631\u062f:</td>\n </tr>\n\t\t{% if (company.tax_id) %}\n <tr>\n <td>Supplier Tax Identification Number:</td>\n <td>\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0645\u0648\u0631\u062f:</td>\n </tr>\n <tr>\n <td>{{ company.tax_id }}</td>\n <td>{{ company.tax_id }}</td>\n </tr>\n {% endif %}\n <tr>\n <td>{{ company.name }}</td>\n <td>{{ company.company_name_in_arabic }} </td>\n </tr>\n \n \n {% if(supplier_address_doc) %}\n <tr>\n <td>{{ supplier_address_doc.address_line1}} </td>\n <td>{{ supplier_address_doc.address_in_arabic}} </td>\n </tr>\n <tr>\n <td>Phone: {{ supplier_address_doc.phone }}</td>\n <td>\u0647\u0627\u062a\u0641: {{ supplier_address_doc.phone }}</td>\n </tr>\n <tr>\n <td>Email: {{ supplier_address_doc.email_id }}</td>\n <td>\u0628\u0631\u064a\u062f \u0627\u0644\u0643\u062a\u0631\u0648\u0646\u064a: {{ supplier_address_doc.email_id }}</td>\n </tr>\n {% endif %}\n \n <!-- Customer Info -->\n <tr>\n <td>CUSTOMER:</td>\n <td>\u0639\u0645\u064a\u0644:</td>\n </tr>\n\t\t{% set customer_tax_id = frappe.db.get_value('Customer', doc.customer, 'tax_id') %}\n\t\t{% if customer_tax_id %}\n <tr>\n <td>Customer Tax Identification Number:</td>\n <td>\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0639\u0645\u064a\u0644:</td>\n </tr>\n <tr>\n <td>{{ customer_tax_id }}</td>\n <td>{{ customer_tax_id }}</td>\n </tr>\n {% endif %}\n <tr>\n <td> {{ doc.customer }}</td>\n <td> {{ doc.customer_name_in_arabic }} </td>\n </tr>\n \n {% if(customer_address) %}\n <tr>\n <td>{{ customer_address.address_line1}} </td>\n <td>{{ customer_address.address_in_arabic}} </td>\n </tr>\n {% endif %}\n \n {% if(customer_shipping_address) %}\n <tr>\n <td>SHIPPING ADDRESS:</td>\n <td>\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0634\u062d\u0646:</td>\n </tr>\n \n <tr>\n <td>{{ customer_shipping_address.address_line1}} </td>\n <td>{{ customer_shipping_address.address_in_arabic}} </td>\n </tr>\n {% endif %}\n \n\t\t{% if(doc.po_no) %}\n <tr>\n <td>OTHER INFORMATION</td>\n <td>\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0623\u062e\u0631\u0649</td>\n </tr>\n \n <tr>\n <td>Purchase Order Number: {{ doc.po_no }}</td>\n <td>\u0631\u0642\u0645 \u0623\u0645\u0631 \u0627\u0644\u0634\u0631\u0627\u0621: {{ doc.po_no }}</td>\n </tr>\n {% endif %}\n \n <tr>\n <td>Payment Due Date: {{ doc.due_date}} </td>\n <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0633\u062a\u062d\u0642\u0627\u0642 \u0627\u0644\u062f\u0641\u0639: {{ doc.due_date}}</td>\n </tr>\n </tbody>\n </table>\n\n <!--Dynamic Colspan for total row columns-->\n {% set col = namespace(one = 2, two = 1) %}\n {% set length = doc.taxes | length %}\n {% set length = length / 2 | round %}\n {% set col.one = col.one + length %}\n {% set col.two = col.two + length %}\n \n {%- if(doc.taxes | length % 2 > 0 ) -%}\n {% set col.two = col.two + 1 %}\n {% endif %}\n \n <!-- Items -->\n {% set total = namespace(amount = 0) %}\n <table class=\"ksa-invoice-table\">\n <thead>\n <tr>\n <th>Nature of goods or services <br />\u0637\u0628\u064a\u0639\u0629 \u0627\u0644\u0633\u0644\u0639 \u0623\u0648 \u0627\u0644\u062e\u062f\u0645\u0627\u062a</th>\n <th>\n Unit price <br />\n \u0633\u0639\u0631 \u0627\u0644\u0648\u062d\u062f\u0629\n </th>\n <th>\n Quantity <br />\n \u0627\u0644\u0643\u0645\u064a\u0629\n </th>\n <th>\n Taxable Amount <br />\n \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u062e\u0627\u0636\u0639 \u0644\u0644\u0636\u0631\u064a\u0628\u0629\n </th>\n \n {% for row in doc.taxes %}\n <th style=\"min-width: 130px\">{{row.description}}</th>\n {% endfor %}\n \n <th>\n Total <br />\n \u0627\u0644\u0645\u062c\u0645\u0648\u0639\n </th>\n </tr>\n </thead>\n <tbody>\n {%- for item in doc.items -%}\n {% set total.amount = item.amount %}\n <tr>\n <td>{{ item.item_code or item.item_name }}</td>\n <td>{{ item.get_formatted(\"rate\") }}</td>\n <td>{{ item.qty }}</td>\n <td>{{ item.get_formatted(\"amount\") }}</td>\n {% for row in doc.taxes %}\n {% set data_object = json.loads(row.item_wise_tax_detail) %}\n {% set key = item.item_code or item.item_name %}\n {% set tax_amount = frappe.utils.flt(data_object[key][1]/doc.conversion_rate, row.precision('tax_amount')) %}\n <td>\n <div class=\"qr-flex\">\n {%- if(data_object[key][0])-%}\n <span>{{ frappe.format(data_object[key][0], {'fieldtype': 'Percent'}) }}</span>\n {%- endif -%}\n <span>\n {%- if(data_object[key][1])-%}\n {{ frappe.format_value(tax_amount, currency=doc.currency) }}</span>\n {% set total.amount = total.amount + tax_amount %}\n {%- endif -%}\n </div>\n </td>\n {% endfor %}\n <td>{{ frappe.format_value(frappe.utils.flt(total.amount, doc.precision('total_taxes_and_charges')), currency=doc.currency) }}</td>\n </tr>\n {%- endfor -%}\n </tbody>\n <tfoot>\n <tr>\n <td>\n {{ doc.get_formatted(\"total\") }} <br />\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n </td>\n \n <td colspan={{ col.one }} class=\"qr-rtl\">\n \u0627\u0644\u0625\u062c\u0645\u0627\u0644\u064a \u0628\u0627\u0633\u062a\u062b\u0646\u0627\u0621 \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n <br />\n \u0625\u062c\u0645\u0627\u0644\u064a \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n </td>\n <td colspan={{ col.two }}>\n Total (Excluding VAT)\n <br />\n Total VAT\n </td>\n <td>\n {{ doc.get_formatted(\"total\") }} <br />\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n </td>\n </tr>\n <tr>\n <td>{{ doc.get_formatted(\"grand_total\") }}</td>\n <td colspan={{ col.one }} class=\"qr-rtl\">\n \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u0645\u0633\u062a\u062d\u0642</td>\n <td colspan={{ col.two }}>Total Amount Due</td>\n <td>{{ doc.get_formatted(\"grand_total\") }}</td>\n </tr>\n </tfoot>\n </table>\n\n\t{%- if doc.terms -%}\n <p>\n {{doc.terms}}\n </p>\n\t{%- endif -%}\n</div>\n",
- "idx": 0,
- "line_breaks": 0,
- "margin_bottom": 15.0,
- "margin_left": 15.0,
- "margin_right": 15.0,
- "margin_top": 15.0,
- "modified": "2021-12-07 13:43:38.018593",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "KSA VAT Invoice",
- "owner": "Administrator",
- "page_number": "Hide",
- "print_format_builder": 0,
- "print_format_builder_beta": 0,
- "print_format_type": "Jinja",
- "raw_printing": 0,
- "show_section_headings": 0,
- "standard": "Yes"
-}
\ No newline at end of file
diff --git a/erpnext/regional/report/ksa_vat/__init__.py b/erpnext/regional/report/ksa_vat/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/report/ksa_vat/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.js b/erpnext/regional/report/ksa_vat/ksa_vat.js
deleted file mode 100644
index 59e72c3..0000000
--- a/erpnext/regional/report/ksa_vat/ksa_vat.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2016, Havenir Solutions and contributors
-// For license information, please see license.txt
-/* eslint-disable */
-
-frappe.query_reports["KSA VAT"] = {
- onload() {
- frappe.breadcrumbs.add('Accounts');
- },
- "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(), -1),
- },
- {
- "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.title=='VAT on Sales' || data.title=='VAT on Purchases')
- && data.title==value) {
- value = $(`<span>${value}</span>`);
- var $value = $(value).css("font-weight", "bold");
- value = $value.wrap("<p></p>").parent().html();
- return value
- }else if (data.title=='Grand Total'){
- if (data.title==value) {
- value = $(`<span>${value}</span>`);
- var $value = $(value).css("font-weight", "bold");
- value = $value.wrap("<p></p>").parent().html();
- return value
- }else{
- value = default_formatter(value, row, column, data);
- value = $(`<span>${value}</span>`);
- var $value = $(value).css("font-weight", "bold");
- value = $value.wrap("<p></p>").parent().html();
- return value
- }
- }else{
- value = default_formatter(value, row, column, data);
- return value;
- }
- },
-};
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.json b/erpnext/regional/report/ksa_vat/ksa_vat.json
deleted file mode 100644
index 036e260..0000000
--- a/erpnext/regional/report/ksa_vat/ksa_vat.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "add_total_row": 0,
- "columns": [],
- "creation": "2021-07-13 08:54:38.000949",
- "disable_prepared_report": 1,
- "disabled": 1,
- "docstatus": 0,
- "doctype": "Report",
- "filters": [],
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2021-08-26 04:14:37.202594",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "KSA VAT",
- "owner": "Administrator",
- "prepared_report": 1,
- "ref_doctype": "GL Entry",
- "report_name": "KSA VAT",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "System Manager"
- },
- {
- "role": "Accounts Manager"
- },
- {
- "role": "Accounts User"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.py b/erpnext/regional/report/ksa_vat/ksa_vat.py
deleted file mode 100644
index 15996d2..0000000
--- a/erpnext/regional/report/ksa_vat/ksa_vat.py
+++ /dev/null
@@ -1,231 +0,0 @@
-# Copyright (c) 2013, Havenir Solutions and contributors
-# For license information, please see license.txt
-
-
-import json
-
-import frappe
-from frappe import _
-from frappe.utils import get_url_to_list
-
-
-def execute(filters=None):
- columns = columns = get_columns()
- data = get_data(filters)
- return columns, data
-
-
-def get_columns():
- return [
- {
- "fieldname": "title",
- "label": _("Title"),
- "fieldtype": "Data",
- "width": 300,
- },
- {
- "fieldname": "amount",
- "label": _("Amount (SAR)"),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 150,
- },
- {
- "fieldname": "adjustment_amount",
- "label": _("Adjustment (SAR)"),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 150,
- },
- {
- "fieldname": "vat_amount",
- "label": _("VAT Amount (SAR)"),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 150,
- },
- {
- "fieldname": "currency",
- "label": _("Currency"),
- "fieldtype": "Currency",
- "width": 150,
- "hidden": 1,
- },
- ]
-
-
-def get_data(filters):
- data = []
-
- # Validate if vat settings exist
- company = filters.get("company")
- company_currency = frappe.get_cached_value("Company", company, "default_currency")
-
- if frappe.db.exists("KSA VAT Setting", company) is None:
- url = get_url_to_list("KSA VAT Setting")
- frappe.msgprint(_('Create <a href="{}">KSA VAT Setting</a> for this company').format(url))
- return data
-
- ksa_vat_setting = frappe.get_doc("KSA VAT Setting", company)
-
- # Sales Heading
- append_data(data, "VAT on Sales", "", "", "", company_currency)
-
- grand_total_taxable_amount = 0
- grand_total_taxable_adjustment_amount = 0
- grand_total_tax = 0
-
- for vat_setting in ksa_vat_setting.ksa_vat_sales_accounts:
- (
- total_taxable_amount,
- total_taxable_adjustment_amount,
- total_tax,
- ) = get_tax_data_for_each_vat_setting(vat_setting, filters, "Sales Invoice")
-
- # Adding results to data
- append_data(
- data,
- vat_setting.title,
- total_taxable_amount,
- total_taxable_adjustment_amount,
- total_tax,
- company_currency,
- )
-
- grand_total_taxable_amount += total_taxable_amount
- grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
- grand_total_tax += total_tax
-
- # Sales Grand Total
- append_data(
- data,
- "Grand Total",
- grand_total_taxable_amount,
- grand_total_taxable_adjustment_amount,
- grand_total_tax,
- company_currency,
- )
-
- # Blank Line
- append_data(data, "", "", "", "", company_currency)
-
- # Purchase Heading
- append_data(data, "VAT on Purchases", "", "", "", company_currency)
-
- grand_total_taxable_amount = 0
- grand_total_taxable_adjustment_amount = 0
- grand_total_tax = 0
-
- for vat_setting in ksa_vat_setting.ksa_vat_purchase_accounts:
- (
- total_taxable_amount,
- total_taxable_adjustment_amount,
- total_tax,
- ) = get_tax_data_for_each_vat_setting(vat_setting, filters, "Purchase Invoice")
-
- # Adding results to data
- append_data(
- data,
- vat_setting.title,
- total_taxable_amount,
- total_taxable_adjustment_amount,
- total_tax,
- company_currency,
- )
-
- grand_total_taxable_amount += total_taxable_amount
- grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
- grand_total_tax += total_tax
-
- # Purchase Grand Total
- append_data(
- data,
- "Grand Total",
- grand_total_taxable_amount,
- grand_total_taxable_adjustment_amount,
- grand_total_tax,
- company_currency,
- )
-
- return data
-
-
-def get_tax_data_for_each_vat_setting(vat_setting, filters, doctype):
- """
- (KSA, {filters}, 'Sales Invoice') => 500, 153, 10 \n
- calculates and returns \n
- total_taxable_amount, total_taxable_adjustment_amount, total_tax"""
- from_date = filters.get("from_date")
- to_date = filters.get("to_date")
-
- # Initiate variables
- total_taxable_amount = 0
- total_taxable_adjustment_amount = 0
- total_tax = 0
- # Fetch All Invoices
- invoices = frappe.get_all(
- doctype,
- filters={"docstatus": 1, "posting_date": ["between", [from_date, to_date]]},
- fields=["name", "is_return"],
- )
-
- for invoice in invoices:
- invoice_items = frappe.get_all(
- f"{doctype} Item",
- filters={
- "docstatus": 1,
- "parent": invoice.name,
- "item_tax_template": vat_setting.item_tax_template,
- },
- fields=["item_code", "net_amount"],
- )
-
- for item in invoice_items:
- # Summing up total taxable amount
- if invoice.is_return == 0:
- total_taxable_amount += item.net_amount
-
- if invoice.is_return == 1:
- total_taxable_adjustment_amount += item.net_amount
-
- # Summing up total tax
- total_tax += get_tax_amount(item.item_code, vat_setting.account, doctype, invoice.name)
-
- return total_taxable_amount, total_taxable_adjustment_amount, total_tax
-
-
-def append_data(data, title, amount, adjustment_amount, vat_amount, company_currency):
- """Returns data with appended value."""
- data.append(
- {
- "title": _(title),
- "amount": amount,
- "adjustment_amount": adjustment_amount,
- "vat_amount": vat_amount,
- "currency": company_currency,
- }
- )
-
-
-def get_tax_amount(item_code, account_head, doctype, parent):
- if doctype == "Sales Invoice":
- tax_doctype = "Sales Taxes and Charges"
-
- elif doctype == "Purchase Invoice":
- tax_doctype = "Purchase Taxes and Charges"
-
- item_wise_tax_detail = frappe.get_value(
- tax_doctype,
- {"docstatus": 1, "parent": parent, "account_head": account_head},
- "item_wise_tax_detail",
- )
-
- tax_amount = 0
- if item_wise_tax_detail and len(item_wise_tax_detail) > 0:
- item_wise_tax_detail = json.loads(item_wise_tax_detail)
- for key, value in item_wise_tax_detail.items():
- if key == item_code:
- tax_amount = value[1]
- break
-
- return tax_amount
diff --git a/erpnext/regional/saudi_arabia/__init__.py b/erpnext/regional/saudi_arabia/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/saudi_arabia/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py
deleted file mode 100644
index 7f41c46..0000000
--- a/erpnext/regional/saudi_arabia/setup.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-from frappe.permissions import add_permission, update_permission_property
-from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import (
- create_ksa_vat_setting,
-)
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-
-
-def setup(company=None, patch=True):
- add_print_formats()
- add_permissions()
- make_custom_fields()
-
-
-def add_print_formats():
- frappe.reload_doc("regional", "print_format", "detailed_tax_invoice", force=True)
- frappe.reload_doc("regional", "print_format", "simplified_tax_invoice", force=True)
- frappe.reload_doc("regional", "print_format", "tax_invoice", force=True)
- frappe.reload_doc("regional", "print_format", "ksa_vat_invoice", force=True)
- frappe.reload_doc("regional", "print_format", "ksa_pos_invoice", force=True)
-
- for d in (
- "Simplified Tax Invoice",
- "Detailed Tax Invoice",
- "Tax Invoice",
- "KSA VAT Invoice",
- "KSA POS Invoice",
- ):
- frappe.db.set_value("Print Format", d, "disabled", 0)
-
-
-def add_permissions():
- """Add Permissions for KSA VAT Setting."""
- add_permission("KSA VAT Setting", "All", 0)
- for role in ("Accounts Manager", "Accounts User", "System Manager"):
- add_permission("KSA VAT Setting", role, 0)
- update_permission_property("KSA VAT Setting", role, 0, "write", 1)
- update_permission_property("KSA VAT Setting", role, 0, "create", 1)
-
- """Enable KSA VAT Report"""
- frappe.db.set_value("Report", "KSA VAT", "disabled", 0)
-
-
-def make_custom_fields():
- """Create Custom fields
- - QR code Image file
- - Company Name in Arabic
- - Address in Arabic
- """
- 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,
- )
-
- purchase_invoice_fields = [
- dict(
- fieldname="company_trn",
- label="Company TRN",
- fieldtype="Read Only",
- insert_after="shipping_address",
- 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,
- ),
- ]
-
- sales_invoice_fields = [
- dict(
- fieldname="company_trn",
- label="Company TRN",
- fieldtype="Read Only",
- insert_after="company_address",
- fetch_from="company.tax_id",
- print_hide=1,
- ),
- 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="ksa_einv_qr",
- label="KSA E-Invoicing QR",
- fieldtype="Attach Image",
- read_only=1,
- no_copy=1,
- hidden=1,
- ),
- ]
-
- custom_fields = {
- "Item": [is_zero_rated, is_exempt],
- "Customer": [
- dict(
- fieldname="customer_name_in_arabic",
- label="Customer Name in Arabic",
- fieldtype="Data",
- insert_after="customer_name",
- ),
- ],
- "Supplier": [
- dict(
- fieldname="supplier_name_in_arabic",
- label="Supplier Name in Arabic",
- fieldtype="Data",
- insert_after="supplier_name",
- ),
- ],
- "Purchase Invoice": purchase_invoice_fields,
- "Purchase Order": purchase_invoice_fields,
- "Purchase Receipt": purchase_invoice_fields,
- "Sales Invoice": sales_invoice_fields,
- "POS Invoice": sales_invoice_fields,
- "Sales Order": sales_invoice_fields,
- "Delivery Note": sales_invoice_fields,
- "Sales Invoice Item": [is_zero_rated, is_exempt],
- "POS Invoice Item": [is_zero_rated, is_exempt],
- "Purchase Invoice Item": [is_zero_rated, is_exempt],
- "Sales Order Item": [is_zero_rated, is_exempt],
- "Delivery Note Item": [is_zero_rated, is_exempt],
- "Quotation Item": [is_zero_rated, is_exempt],
- "Purchase Order Item": [is_zero_rated, is_exempt],
- "Purchase Receipt Item": [is_zero_rated, is_exempt],
- "Supplier Quotation Item": [is_zero_rated, is_exempt],
- "Address": [
- dict(
- fieldname="address_in_arabic",
- label="Address in Arabic",
- fieldtype="Data",
- insert_after="address_line2",
- )
- ],
- "Company": [
- dict(
- fieldname="company_name_in_arabic",
- label="Company Name In Arabic",
- fieldtype="Data",
- insert_after="company_name",
- )
- ],
- }
-
- create_custom_fields(custom_fields, ignore_validate=True, update=True)
-
-
-def update_regional_tax_settings(country, company):
- create_ksa_vat_setting(company)
diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py
deleted file mode 100644
index cac5ec1..0000000
--- a/erpnext/regional/saudi_arabia/utils.py
+++ /dev/null
@@ -1,169 +0,0 @@
-import io
-import os
-from base64 import b64encode
-
-import frappe
-from frappe import _
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-from frappe.utils.data import add_to_date, get_time, getdate
-from pyqrcode import create as qr_create
-
-from erpnext import get_region
-
-
-def create_qr_code(doc, method=None):
- region = get_region(doc.company)
- if region not in ["Saudi Arabia"]:
- return
-
- # if QR Code field not present, create it. Invoices without QR are invalid as per law.
- if not hasattr(doc, "ksa_einv_qr"):
- create_custom_fields(
- {
- doc.doctype: [
- dict(
- fieldname="ksa_einv_qr",
- label="KSA E-Invoicing QR",
- fieldtype="Attach Image",
- read_only=1,
- no_copy=1,
- hidden=1,
- )
- ]
- }
- )
-
- # Don't create QR Code if it already exists
- qr_code = doc.get("ksa_einv_qr")
- if qr_code and frappe.db.exists({"doctype": "File", "file_url": qr_code}):
- return
-
- meta = frappe.get_meta(doc.doctype)
-
- if "ksa_einv_qr" in [d.fieldname for d in meta.get_image_fields()]:
- """TLV conversion for
- 1. Seller's Name
- 2. VAT Number
- 3. Time Stamp
- 4. Invoice Amount
- 5. VAT Amount
- """
- tlv_array = []
- # Sellers Name
-
- seller_name = frappe.db.get_value("Company", doc.company, "company_name_in_arabic")
-
- if not seller_name:
- frappe.throw(_("Arabic name missing for {} in the company document").format(doc.company))
-
- tag = bytes([1]).hex()
- length = bytes([len(seller_name.encode("utf-8"))]).hex()
- value = seller_name.encode("utf-8").hex()
- tlv_array.append("".join([tag, length, value]))
-
- # VAT Number
- tax_id = frappe.db.get_value("Company", doc.company, "tax_id")
- if not tax_id:
- frappe.throw(_("Tax ID missing for {} in the company document").format(doc.company))
-
- tag = bytes([2]).hex()
- length = bytes([len(tax_id)]).hex()
- value = tax_id.encode("utf-8").hex()
- tlv_array.append("".join([tag, length, value]))
-
- # Time Stamp
- posting_date = getdate(doc.posting_date)
- time = get_time(doc.posting_time)
- seconds = time.hour * 60 * 60 + time.minute * 60 + time.second
- time_stamp = add_to_date(posting_date, seconds=seconds)
- time_stamp = time_stamp.strftime("%Y-%m-%dT%H:%M:%SZ")
-
- tag = bytes([3]).hex()
- length = bytes([len(time_stamp)]).hex()
- value = time_stamp.encode("utf-8").hex()
- tlv_array.append("".join([tag, length, value]))
-
- # Invoice Amount
- invoice_amount = str(doc.base_grand_total)
- tag = bytes([4]).hex()
- length = bytes([len(invoice_amount)]).hex()
- value = invoice_amount.encode("utf-8").hex()
- tlv_array.append("".join([tag, length, value]))
-
- # VAT Amount
- vat_amount = str(get_vat_amount(doc))
-
- tag = bytes([5]).hex()
- length = bytes([len(vat_amount)]).hex()
- value = vat_amount.encode("utf-8").hex()
- tlv_array.append("".join([tag, length, value]))
-
- # Joining bytes into one
- tlv_buff = "".join(tlv_array)
-
- # base64 conversion for QR Code
- base64_string = b64encode(bytes.fromhex(tlv_buff)).decode()
-
- qr_image = io.BytesIO()
- url = qr_create(base64_string, error="L")
- url.png(qr_image, scale=2, quiet_zone=1)
-
- name = frappe.generate_hash(doc.name, 5)
-
- # making file
- filename = f"QRCode-{name}.png".replace(os.path.sep, "__")
- _file = frappe.get_doc(
- {
- "doctype": "File",
- "file_name": filename,
- "is_private": 0,
- "content": qr_image.getvalue(),
- "attached_to_doctype": doc.get("doctype"),
- "attached_to_name": doc.get("name"),
- "attached_to_field": "ksa_einv_qr",
- }
- )
-
- _file.save()
-
- # assigning to document
- doc.db_set("ksa_einv_qr", _file.file_url)
- doc.notify_update()
-
-
-def get_vat_amount(doc):
- vat_settings = frappe.db.get_value("KSA VAT Setting", {"company": doc.company})
- vat_accounts = []
- vat_amount = 0
-
- if vat_settings:
- vat_settings_doc = frappe.get_cached_doc("KSA VAT Setting", vat_settings)
-
- for row in vat_settings_doc.get("ksa_vat_sales_accounts"):
- vat_accounts.append(row.account)
-
- for tax in doc.get("taxes"):
- if tax.account_head in vat_accounts:
- vat_amount += tax.base_tax_amount
-
- return vat_amount
-
-
-def delete_qr_code_file(doc, method=None):
- region = get_region(doc.company)
- if region not in ["Saudi Arabia"]:
- return
-
- if hasattr(doc, "ksa_einv_qr"):
- if doc.get("ksa_einv_qr"):
- file_doc = frappe.get_list("File", {"file_url": doc.get("ksa_einv_qr")})
- if len(file_doc):
- frappe.delete_doc("File", file_doc[0].name)
-
-
-def delete_vat_settings_for_company(doc, method=None):
- if doc.country != "Saudi Arabia":
- return
-
- if frappe.db.exists("KSA VAT Setting", doc.name):
- frappe.delete_doc("KSA VAT Setting", doc.name)
diff --git a/erpnext/regional/saudi_arabia/wizard/__init__.py b/erpnext/regional/saudi_arabia/wizard/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/saudi_arabia/wizard/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/saudi_arabia/wizard/data/__init__.py b/erpnext/regional/saudi_arabia/wizard/data/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/saudi_arabia/wizard/data/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json b/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json
deleted file mode 100644
index 60951a9..0000000
--- a/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json
+++ /dev/null
@@ -1,47 +0,0 @@
-[
- {
- "type": "Sales Account",
- "accounts": [
- {
- "title": "Standard rated Sales",
- "item_tax_template": "KSA VAT 5%",
- "account": "VAT 5%"
- },
- {
- "title": "Zero rated domestic sales",
- "item_tax_template": "KSA VAT Zero",
- "account": "VAT Zero"
- },
- {
- "title": "Exempted sales",
- "item_tax_template": "KSA VAT Exempted",
- "account": "VAT Exempted"
- }
- ]
- },
- {
- "type": "Purchase Account",
- "accounts": [
- {
- "title": "Standard rated domestic purchases",
- "item_tax_template": "KSA VAT 5%",
- "account": "VAT 5%"
- },
- {
- "title": "Imports subject to VAT paid at customs",
- "item_tax_template": "KSA Excise 50%",
- "account": "Excise 50%"
- },
- {
- "title": "Zero rated purchases",
- "item_tax_template": "KSA VAT Zero",
- "account": "VAT Zero"
- },
- {
- "title": "Exempted purchases",
- "item_tax_template": "KSA VAT Exempted",
- "account": "VAT Exempted"
- }
- ]
- }
-]
\ No newline at end of file
diff --git a/erpnext/regional/saudi_arabia/wizard/operations/__init__.py b/erpnext/regional/saudi_arabia/wizard/operations/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/saudi_arabia/wizard/operations/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
deleted file mode 100644
index 66d9df2..0000000
--- a/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import json
-import os
-
-import frappe
-
-
-def create_ksa_vat_setting(company):
- """On creation of first company. Creates KSA VAT Setting"""
-
- company = frappe.get_doc("Company", company)
-
- file_path = os.path.join(os.path.dirname(__file__), "..", "data", "ksa_vat_settings.json")
- with open(file_path, "r") as json_file:
- account_data = json.load(json_file)
-
- # Creating KSA VAT Setting
- ksa_vat_setting = frappe.get_doc({"doctype": "KSA VAT Setting", "company": company.name})
-
- for data in account_data:
- if data["type"] == "Sales Account":
- for row in data["accounts"]:
- item_tax_template = row["item_tax_template"]
- account = row["account"]
- ksa_vat_setting.append(
- "ksa_vat_sales_accounts",
- {
- "title": row["title"],
- "item_tax_template": f"{item_tax_template} - {company.abbr}",
- "account": f"{account} - {company.abbr}",
- },
- )
-
- elif data["type"] == "Purchase Account":
- for row in data["accounts"]:
- item_tax_template = row["item_tax_template"]
- account = row["account"]
- ksa_vat_setting.append(
- "ksa_vat_purchase_accounts",
- {
- "title": row["title"],
- "item_tax_template": f"{item_tax_template} - {company.abbr}",
- "account": f"{account} - {company.abbr}",
- },
- )
-
- ksa_vat_setting.save()
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index fb64772..ee07525 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -309,9 +309,12 @@
make_work_order() {
var me = this;
- this.frm.call({
- doc: this.frm.doc,
- method: 'get_work_order_items',
+ me.frm.call({
+ method: "erpnext.selling.doctype.sales_order.sales_order.get_work_order_items",
+ args: {
+ sales_order: this.frm.docname,
+ },
+ freeze: true,
callback: function(r) {
if(!r.message) {
frappe.msgprint({
@@ -321,14 +324,7 @@
});
return;
}
- else if(!r.message) {
- frappe.msgprint({
- title: __('Work Order not created'),
- message: __('Work Order already created for all items with BOM'),
- indicator: 'orange'
- });
- return;
- } else {
+ else {
const fields = [{
label: 'Items',
fieldtype: 'Table',
@@ -429,9 +425,9 @@
make_raw_material_request() {
var me = this;
this.frm.call({
- doc: this.frm.doc,
- method: 'get_work_order_items',
+ method: "erpnext.selling.doctype.sales_order.sales_order.get_work_order_items",
args: {
+ sales_order: this.frm.docname,
for_raw_material_request: 1
},
callback: function(r) {
@@ -450,6 +446,7 @@
}
make_raw_material_request_dialog(r) {
+ var me = this;
var fields = [
{fieldtype:'Check', fieldname:'include_exploded_items',
label: __('Include Exploded Items')},
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index ca6a51a..385d0f3 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -6,11 +6,12 @@
import frappe
import frappe.utils
-from frappe import _
+from frappe import _, qb
from frappe.contacts.doctype.address.address import get_company_address
from frappe.desk.notifications import clear_doctype_notifications
from frappe.model.mapper import get_mapped_doc
from frappe.model.utils import get_fetch_values
+from frappe.query_builder.functions import Sum
from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, nowdate, strip_html
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
@@ -414,51 +415,6 @@
self.indicator_color = "green"
self.indicator_title = _("Paid")
- @frappe.whitelist()
- def get_work_order_items(self, for_raw_material_request=0):
- """Returns items with BOM that already do not have a linked work order"""
- items = []
- item_codes = [i.item_code for i in self.items]
- product_bundle_parents = [
- pb.new_item_code
- for pb in frappe.get_all(
- "Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"]
- )
- ]
-
- for table in [self.items, self.packed_items]:
- for i in table:
- bom = get_default_bom(i.item_code)
- stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty
-
- if not for_raw_material_request:
- total_work_order_qty = flt(
- frappe.db.sql(
- """select sum(qty) from `tabWork Order`
- where production_item=%s and sales_order=%s and sales_order_item = %s and docstatus<2""",
- (i.item_code, self.name, i.name),
- )[0][0]
- )
- pending_qty = stock_qty - total_work_order_qty
- else:
- pending_qty = stock_qty
-
- if pending_qty and i.item_code not in product_bundle_parents:
- items.append(
- dict(
- name=i.name,
- item_code=i.item_code,
- description=i.description,
- bom=bom or "",
- warehouse=i.warehouse,
- pending_qty=pending_qty,
- required_qty=pending_qty if for_raw_material_request else 0,
- sales_order_item=i.name,
- )
- )
-
- return items
-
def on_recurring(self, reference_doc, auto_repeat_doc):
def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date):
delivery_date = auto_repeat_doc.get_next_schedule_date(schedule_date=ref_doc_delivery_date)
@@ -1350,3 +1306,57 @@
return
frappe.db.set_value("Sales Order Item", sales_order_item, "produced_qty", total_produced_qty)
+
+
+@frappe.whitelist()
+def get_work_order_items(sales_order, for_raw_material_request=0):
+ """Returns items with BOM that already do not have a linked work order"""
+ if sales_order:
+ so = frappe.get_doc("Sales Order", sales_order)
+
+ wo = qb.DocType("Work Order")
+
+ items = []
+ item_codes = [i.item_code for i in so.items]
+ product_bundle_parents = [
+ pb.new_item_code
+ for pb in frappe.get_all(
+ "Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"]
+ )
+ ]
+
+ for table in [so.items, so.packed_items]:
+ for i in table:
+ bom = get_default_bom(i.item_code)
+ stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty
+
+ if not for_raw_material_request:
+ total_work_order_qty = flt(
+ qb.from_(wo)
+ .select(Sum(wo.qty))
+ .where(
+ (wo.production_item == i.item_code)
+ & (wo.sales_order == so.name) * (wo.sales_order_item == i.name)
+ & (wo.docstatus.lte(2))
+ )
+ .run()[0][0]
+ )
+ pending_qty = stock_qty - total_work_order_qty
+ else:
+ pending_qty = stock_qty
+
+ if pending_qty and i.item_code not in product_bundle_parents:
+ items.append(
+ dict(
+ name=i.name,
+ item_code=i.item_code,
+ description=i.description,
+ bom=bom or "",
+ warehouse=i.warehouse,
+ pending_qty=pending_qty,
+ required_qty=pending_qty if for_raw_material_request else 0,
+ sales_order_item=i.name,
+ )
+ )
+
+ return items
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index d4d7c58..627914f 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1217,6 +1217,8 @@
self.assertTrue(si.get("payment_schedule"))
def test_make_work_order(self):
+ from erpnext.selling.doctype.sales_order.sales_order import get_work_order_items
+
# Make a new Sales Order
so = make_sales_order(
**{
@@ -1230,7 +1232,7 @@
# Raise Work Orders
po_items = []
so_item_name = {}
- for item in so.get_work_order_items():
+ for item in get_work_order_items(so.name):
po_items.append(
{
"warehouse": item.get("warehouse"),
@@ -1448,6 +1450,7 @@
from erpnext.controllers.item_variant import create_variant
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+ from erpnext.selling.doctype.sales_order.sales_order import get_work_order_items
make_item( # template item
"Test-WO-Tshirt",
@@ -1487,7 +1490,7 @@
]
}
)
- wo_items = so.get_work_order_items()
+ wo_items = get_work_order_items(so.name)
self.assertEqual(wo_items[0].get("item_code"), "Test-WO-Tshirt-R")
self.assertEqual(wo_items[0].get("bom"), red_var_bom.name)
@@ -1497,6 +1500,8 @@
self.assertEqual(wo_items[1].get("bom"), template_bom.name)
def test_request_for_raw_materials(self):
+ from erpnext.selling.doctype.sales_order.sales_order import get_work_order_items
+
item = make_item(
"_Test Finished Item",
{
@@ -1529,7 +1534,7 @@
so = make_sales_order(**{"item_list": [{"item_code": item.item_code, "qty": 1, "rate": 1000}]})
so.submit()
mr_dict = frappe._dict()
- items = so.get_work_order_items(1)
+ items = get_work_order_items(so.name, 1)
mr_dict["items"] = items
mr_dict["include_exploded_items"] = 0
mr_dict["ignore_existing_ordered_qty"] = 1
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
index 44c4d54..2624db3 100644
--- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
@@ -216,7 +216,7 @@
)
if filters.get("item_group"):
- query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_group))
+ query = query.where(db_so_item.item_group == filters.item_group)
if filters.get("from_date"):
query = query.where(db_so.transaction_date >= filters.from_date)
@@ -225,7 +225,7 @@
query = query.where(db_so.transaction_date <= filters.to_date)
if filters.get("item_code"):
- query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_code))
+ query = query.where(db_so_item.item_code == filters.item_code)
if filters.get("customer"):
query = query.where(db_so.customer == filters.customer)
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 8ff01f5..5ce6e9c 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -418,8 +418,6 @@
callback: function(r) {
if(r.message) {
frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message);
- } else {
- frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message);
}
}
});
diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py
index facefa3..ece5a7d 100755
--- a/erpnext/setup/doctype/employee/employee.py
+++ b/erpnext/setup/doctype/employee/employee.py
@@ -8,7 +8,6 @@
get_doc_permissions,
has_permission,
remove_user_permission,
- set_user_permission_if_allowed,
)
from frappe.utils import cstr, getdate, today, validate_email_address
from frappe.utils.nestedset import NestedSet
@@ -96,7 +95,7 @@
return
add_user_permission("Employee", self.name, self.user_id)
- set_user_permission_if_allowed("Company", self.company, self.user_id)
+ add_user_permission("Company", self.company, self.user_id)
def update_user(self):
# add employee role if missing
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
index 4256a7d..481a3a5 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -3,13 +3,17 @@
import frappe
-from frappe import _
+from frappe import _, qb
from frappe.desk.notifications import clear_notifications
from frappe.model.document import Document
-from frappe.utils import cint
+from frappe.utils import cint, create_batch
class TransactionDeletionRecord(Document):
+ def __init__(self, *args, **kwargs):
+ super(TransactionDeletionRecord, self).__init__(*args, **kwargs)
+ self.batch_size = 5000
+
def validate(self):
frappe.only_for("System Manager")
self.validate_doctypes_to_be_ignored()
@@ -155,8 +159,9 @@
"DocField", filters={"fieldtype": "Table", "parent": doctype}, pluck="options"
)
- for table in child_tables:
- frappe.db.delete(table, {"parent": ["in", parent_docs_to_be_deleted]})
+ for batch in create_batch(parent_docs_to_be_deleted, self.batch_size):
+ for table in child_tables:
+ frappe.db.delete(table, {"parent": ["in", batch]})
def delete_docs_linked_with_specified_company(self, doctype, company_fieldname):
frappe.db.delete(doctype, {company_fieldname: self.company})
@@ -181,13 +186,16 @@
frappe.db.sql("""update `tabSeries` set current = %s where name=%s""", (last, prefix))
def delete_version_log(self, doctype, company_fieldname):
- frappe.db.sql(
- """delete from `tabVersion` where ref_doctype=%s and docname in
- (select name from `tab{0}` where `{1}`=%s)""".format(
- doctype, company_fieldname
- ),
- (doctype, self.company),
- )
+ dt = qb.DocType(doctype)
+ names = qb.from_(dt).select(dt.name).where(dt[company_fieldname] == self.company).run(as_list=1)
+ names = [x[0] for x in names]
+
+ if names:
+ versions = qb.DocType("Version")
+ for batch in create_batch(names, self.batch_size):
+ qb.from_(versions).delete().where(
+ (versions.ref_doctype == doctype) & (versions.docname.isin(batch))
+ ).run()
def delete_communications(self, doctype, company_fieldname):
reference_docs = frappe.get_all(doctype, filters={company_fieldname: self.company})
@@ -199,7 +207,8 @@
)
communication_names = [c.name for c in communications]
- frappe.delete_doc("Communication", communication_names, ignore_permissions=True)
+ for batch in create_batch(communication_names, self.batch_size):
+ frappe.delete_doc("Communication", batch, ignore_permissions=True)
@frappe.whitelist()
diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json
index 45e39c5..5750914 100644
--- a/erpnext/setup/setup_wizard/data/country_wise_tax.json
+++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json
@@ -4015,34 +4015,6 @@
"tax_rate": 18.00
}
},
-
- "Saudi Arabia": {
- "KSA VAT 15%": {
- "account_name": "VAT 15%",
- "tax_rate": 15.00
- },
- "KSA VAT 5%": {
- "account_name": "VAT 5%",
- "tax_rate": 5.00
- },
- "KSA VAT Zero": {
- "account_name": "VAT Zero",
- "tax_rate": 0.00
- },
- "KSA VAT Exempted": {
- "account_name": "VAT Exempted",
- "tax_rate": 0.00
- },
- "KSA Excise 50%": {
- "account_name": "Excise 50%",
- "tax_rate": 50.00
- },
- "KSA Excise 100%": {
- "account_name": "Excise 100%",
- "tax_rate": 100.00
- }
- },
-
"Serbia": {
"Serbia Tax": {
"account_name": "VAT",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index ea3cf19..ae56645 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -97,12 +97,12 @@
}
if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
- let internal = me.frm.doc.is_internal_customer;
+ let internal = frm.doc.is_internal_customer;
if (internal) {
- let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" :
+ let button_label = (frm.doc.company === frm.doc.represents_company) ? "Internal Purchase Receipt" :
"Inter Company Purchase Receipt";
- me.frm.add_custom_button(button_label, function() {
+ frm.add_custom_button(__(button_label), function() {
frappe.model.open_mapped_doc({
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
frm: frm,
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 5bcb05a..9a9ddf4 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -33,6 +33,9 @@
'Material Request': () => {
open_form(frm, "Material Request", "Material Request Item", "items");
},
+ 'Stock Entry': () => {
+ open_form(frm, "Stock Entry", "Stock Entry Detail", "items");
+ },
};
},
@@ -893,6 +896,9 @@
new_child_doc.item_name = frm.doc.item_name;
new_child_doc.uom = frm.doc.stock_uom;
new_child_doc.description = frm.doc.description;
+ if (!new_child_doc.qty) {
+ new_child_doc.qty = 1.0;
+ }
frappe.run_serially([
() => frappe.ui.form.make_quick_entry(doctype, null, null, new_doc),
diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js
index 12cf6cf..ce489ff 100644
--- a/erpnext/stock/doctype/item_price/item_price.js
+++ b/erpnext/stock/doctype/item_price/item_price.js
@@ -2,7 +2,18 @@
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Item Price", {
- onload: function (frm) {
+ setup(frm) {
+ frm.set_query("item_code", function() {
+ return {
+ filters: {
+ "disabled": 0,
+ "has_variants": 0
+ }
+ };
+ });
+ },
+
+ onload(frm) {
// Fetch price list details
frm.add_fetch("price_list", "buying", "buying");
frm.add_fetch("price_list", "selling", "selling");
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index bcd31ad..54d1ae6 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -3,7 +3,7 @@
import frappe
-from frappe import _
+from frappe import _, bold
from frappe.model.document import Document
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Cast_
@@ -21,6 +21,7 @@
self.update_price_list_details()
self.update_item_details()
self.check_duplicates()
+ self.validate_item_template()
def validate_item(self):
if not frappe.db.exists("Item", self.item_code):
@@ -49,6 +50,12 @@
"Item", self.item_code, ["item_name", "description"]
)
+ def validate_item_template(self):
+ if frappe.get_cached_value("Item", self.item_code, "has_variants"):
+ msg = f"Item Price cannot be created for the template item {bold(self.item_code)}"
+
+ frappe.throw(_(msg))
+
def check_duplicates(self):
item_price = frappe.qb.DocType("Item Price")
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index 30d933e..8fd4938 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -16,6 +16,28 @@
frappe.db.sql("delete from `tabItem Price`")
make_test_records_for_doctype("Item Price", force=True)
+ def test_template_item_price(self):
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ item = make_item(
+ "Test Template Item 1",
+ {
+ "has_variants": 1,
+ "variant_based_on": "Manufacturer",
+ },
+ )
+
+ doc = frappe.get_doc(
+ {
+ "doctype": "Item Price",
+ "price_list": "_Test Price List",
+ "item_code": item.name,
+ "price_list_rate": 100,
+ }
+ )
+
+ self.assertRaises(frappe.ValidationError, doc.save)
+
def test_duplicate_item(self):
doc = frappe.copy_doc(test_records[0])
self.assertRaises(ItemPriceDuplicateItem, doc.save)
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index b3af309..111a0861 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -55,7 +55,6 @@
self.get_items_from_purchase_receipts()
self.set_applicable_charges_on_item()
- self.validate_applicable_charges_for_item()
def check_mandatory(self):
if not self.get("purchase_receipts"):
@@ -115,6 +114,13 @@
total_item_cost += item.get(based_on_field)
for item in self.get("items"):
+ if not total_item_cost and not item.get(based_on_field):
+ frappe.throw(
+ _(
+ "It's not possible to distribute charges equally when total amount is zero, please set 'Distribute Charges Based On' as 'Quantity'"
+ )
+ )
+
item.applicable_charges = flt(
flt(item.get(based_on_field)) * (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
item.precision("applicable_charges"),
@@ -162,6 +168,7 @@
)
def on_submit(self):
+ self.validate_applicable_charges_for_item()
self.update_landed_cost()
def on_cancel(self):
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 979b5c4..00fa168 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -175,6 +175,59 @@
)
self.assertEqual(last_sle_after_landed_cost.stock_value - last_sle.stock_value, 50.0)
+ def test_landed_cost_voucher_for_zero_purchase_rate(self):
+ "Test impact of LCV on future stock balances."
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ item = make_item("LCV Stock Item", {"is_stock_item": 1})
+ warehouse = "Stores - _TC"
+
+ pr = make_purchase_receipt(
+ item_code=item.name,
+ warehouse=warehouse,
+ qty=10,
+ rate=0,
+ posting_date=add_days(frappe.utils.nowdate(), -2),
+ )
+
+ self.assertEqual(
+ frappe.db.get_value(
+ "Stock Ledger Entry",
+ {"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "is_cancelled": 0},
+ "stock_value_difference",
+ ),
+ 0,
+ )
+
+ lcv = make_landed_cost_voucher(
+ company=pr.company,
+ receipt_document_type="Purchase Receipt",
+ receipt_document=pr.name,
+ charges=100,
+ distribute_charges_based_on="Distribute Manually",
+ do_not_save=True,
+ )
+
+ lcv.get_items_from_purchase_receipts()
+ lcv.items[0].applicable_charges = 100
+ lcv.save()
+ lcv.submit()
+
+ self.assertTrue(
+ frappe.db.exists(
+ "Stock Ledger Entry",
+ {"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "is_cancelled": 0},
+ )
+ )
+ self.assertEqual(
+ frappe.db.get_value(
+ "Stock Ledger Entry",
+ {"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "is_cancelled": 0},
+ "stock_value_difference",
+ ),
+ 100,
+ )
+
def test_landed_cost_voucher_against_purchase_invoice(self):
pi = make_purchase_invoice(
@@ -516,7 +569,7 @@
lcv = frappe.new_doc("Landed Cost Voucher")
lcv.company = args.company or "_Test Company"
- lcv.distribute_charges_based_on = "Amount"
+ lcv.distribute_charges_based_on = args.distribute_charges_based_on or "Amount"
lcv.set(
"purchase_receipts",
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 6426fe8..dcbc460 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -587,6 +587,9 @@
def set_missing_values(source, target):
target.purpose = source.material_request_type
+ target.from_warehouse = source.set_from_warehouse
+ target.to_warehouse = source.set_warehouse
+
if source.job_card:
target.purpose = "Material Transfer for Manufacture"
@@ -722,6 +725,7 @@
def make_in_transit_stock_entry(source_name, in_transit_warehouse):
ste_doc = make_stock_entry(source_name)
ste_doc.add_to_transit = 1
+ ste_doc.to_warehouse = in_transit_warehouse
for row in ste_doc.items:
row.t_warehouse = in_transit_warehouse
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index af0d148..c8a4bd3 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -473,7 +473,7 @@
)
divisional_loss = flt(
- valuation_amount_as_per_doc - stock_value_diff, d.precision("base_net_amount")
+ valuation_amount_as_per_doc - flt(stock_value_diff), d.precision("base_net_amount")
)
if divisional_loss:
@@ -887,18 +887,10 @@
# Update Billing % based on pending accepted qty
total_amount, total_billed_amount = 0, 0
- for item in pr_doc.items:
- return_data = frappe.get_all(
- "Purchase Receipt",
- fields=["sum(abs(`tabPurchase Receipt Item`.qty)) as qty"],
- filters=[
- ["Purchase Receipt", "docstatus", "=", 1],
- ["Purchase Receipt", "is_return", "=", 1],
- ["Purchase Receipt Item", "purchase_receipt_item", "=", item.name],
- ],
- )
+ item_wise_returned_qty = get_item_wise_returned_qty(pr_doc)
- returned_qty = return_data[0].qty if return_data else 0
+ for item in pr_doc.items:
+ returned_qty = flt(item_wise_returned_qty.get(item.name))
returned_amount = flt(returned_qty) * flt(item.rate)
pending_amount = flt(item.amount) - returned_amount
total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt
@@ -915,6 +907,27 @@
pr_doc.notify_update()
+def get_item_wise_returned_qty(pr_doc):
+ items = [d.name for d in pr_doc.items]
+
+ return frappe._dict(
+ frappe.get_all(
+ "Purchase Receipt",
+ fields=[
+ "`tabPurchase Receipt Item`.purchase_receipt_item",
+ "sum(abs(`tabPurchase Receipt Item`.qty)) as qty",
+ ],
+ filters=[
+ ["Purchase Receipt", "docstatus", "=", 1],
+ ["Purchase Receipt", "is_return", "=", 1],
+ ["Purchase Receipt Item", "purchase_receipt_item", "in", items],
+ ],
+ group_by="`tabPurchase Receipt Item`.purchase_receipt_item",
+ as_list=1,
+ )
+ )
+
+
@frappe.whitelist()
def make_purchase_invoice(source_name, target_doc=None):
from erpnext.accounts.party import get_payment_terms_template
@@ -1121,13 +1134,25 @@
account.expense_account, {"amount": 0.0, "base_amount": 0.0}
)
- item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account][
- "amount"
- ] += (account.amount * item.get(based_on_field) / total_item_cost)
+ if total_item_cost > 0:
+ item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
+ account.expense_account
+ ]["amount"] += (
+ account.amount * item.get(based_on_field) / total_item_cost
+ )
- item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account][
- "base_amount"
- ] += (account.base_amount * item.get(based_on_field) / total_item_cost)
+ item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
+ account.expense_account
+ ]["base_amount"] += (
+ account.base_amount * item.get(based_on_field) / total_item_cost
+ )
+ else:
+ item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
+ account.expense_account
+ ]["amount"] += item.applicable_charges
+ item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
+ account.expense_account
+ ]["base_amount"] += item.applicable_charges
return item_account_wise_cost
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 557bb59..7a350b9 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -859,7 +859,8 @@
"label": "Purchase Receipt Item",
"no_copy": 1,
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "search_index": 1
},
{
"collapsible": 1,
@@ -974,7 +975,8 @@
"label": "Purchase Invoice Item",
"no_copy": 1,
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "search_index": 1
},
{
"fieldname": "product_bundle",
@@ -1010,7 +1012,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2022-11-02 12:49:28.746701",
+ "modified": "2023-01-18 15:48:58.114923",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 8b63c0f..da17cde 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -306,7 +306,7 @@
query = query.where(sle.item_code.isin(items))
for field in ["voucher_no", "batch_no", "project", "company"]:
- if filters.get(field):
+ if filters.get(field) and field not in inventory_dimension_fields:
query = query.where(sle[field] == filters.get(field))
query = apply_warehouse_filter(query, sle, filters)
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index 5a0a863..bec3ce2 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -9916,3 +9916,5 @@
Delivered at Place,Geliefert benannter Ort,
Delivered at Place Unloaded,Geliefert benannter Ort entladen,
Delivered Duty Paid,Geliefert verzollt,
+Discount Validity,Frist für den Rabatt,
+Discount Validity Based On,Frist für den Rabatt berechnet sich nach,