diff --git a/.eslintrc b/.eslintrc
index 757aa3caaf..d6f0f49363 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -5,7 +5,7 @@
"es6": true
},
"parserOptions": {
- "ecmaVersion": 6,
+ "ecmaVersion": 9,
"sourceType": "module"
},
"extends": "eslint:recommended",
@@ -15,6 +15,14 @@
"tab",
{ "SwitchCase": 1 }
],
+ "brace-style": [
+ "error",
+ "1tbs"
+ ],
+ "space-unary-ops": [
+ "error",
+ { "words": true }
+ ],
"linebreak-style": [
"error",
"unix"
@@ -44,12 +52,10 @@
"no-control-regex": [
"off"
],
- "spaced-comment": [
- "warn"
- ],
- "no-trailing-spaces": [
- "warn"
- ]
+ "space-before-blocks": "warn",
+ "keyword-spacing": "warn",
+ "comma-spacing": "warn",
+ "key-spacing": "warn"
},
"root": true,
"globals": {
diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json
index b0371e7c09..b2a3f83e5f 100644
--- a/erpnext/accounts/desk_page/accounting/accounting.json
+++ b/erpnext/accounts/desk_page/accounting/accounting.json
@@ -23,7 +23,7 @@
{
"hidden": 0,
"label": "Reports",
- "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n }\n]"
+ "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"DATEV Export\",\n \"name\": \"DATEV\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
@@ -43,7 +43,7 @@
{
"hidden": 0,
"label": "Bank Statement",
- "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
@@ -79,6 +79,11 @@
"hidden": 0,
"label": "Profitability",
"links": "[\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Gross Profit\",\n \"name\": \"Gross Profit\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profitability Analysis\",\n \"name\": \"Profitability Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"type\": \"report\"\n }\n]"
+ },
+ {
+ "hidden": 0,
+ "label": "Value-Added Tax (VAT UAE)",
+ "links": "[\n {\n \"country\": \"United Arab Emirates\",\n \"label\": \"UAE VAT Settings\",\n \"name\": \"UAE VAT Settings\",\n \"type\": \"doctype\"\n },\n {\n \"country\": \"United Arab Emirates\",\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n }\n\n]"
}
],
"category": "Modules",
@@ -98,7 +103,7 @@
"idx": 0,
"is_standard": 1,
"label": "Accounting",
- "modified": "2020-10-08 20:31:46.022470",
+ "modified": "2020-11-11 18:35:11.542909",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
@@ -108,7 +113,7 @@
"pin_to_top": 0,
"shortcuts": [
{
- "label": "Chart of Accounts",
+ "label": "Chart Of Accounts",
"link_to": "Account",
"type": "DocType"
},
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js
index 152e17dbc8..bc77dac1cd 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js
@@ -9,11 +9,7 @@ frappe.ui.form.on('Fiscal Year', {
}
},
refresh: function (frm) {
- let doc = frm.doc;
- frm.toggle_enable('year_start_date', doc.__islocal);
- frm.toggle_enable('year_end_date', doc.__islocal);
-
- if (!doc.__islocal && (doc.name != frappe.sys_defaults.fiscal_year)) {
+ if (!frm.doc.__islocal && (frm.doc.name != frappe.sys_defaults.fiscal_year)) {
frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm));
frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'"));
} else {
@@ -24,8 +20,10 @@ frappe.ui.form.on('Fiscal Year', {
return frm.call('set_as_default');
},
year_start_date: function(frm) {
- let year_end_date =
- frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1);
- frm.set_value("year_end_date", year_end_date);
+ if (!frm.doc.is_short_year) {
+ let year_end_date =
+ frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1);
+ frm.set_value("year_end_date", year_end_date);
+ }
},
});
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
index 4ca9f6b96f..5ab91f2506 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
@@ -1,347 +1,126 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 0,
- "autoname": "field:year",
- "beta": 0,
- "creation": "2013-01-22 16:50:25",
- "custom": 0,
- "description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 0,
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "field:year",
+ "creation": "2013-01-22 16:50:25",
+ "description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "engine": "InnoDB",
+ "field_order": [
+ "year",
+ "disabled",
+ "is_short_year",
+ "year_start_date",
+ "year_end_date",
+ "companies",
+ "auto_created"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "For e.g. 2012, 2012-13",
- "fieldname": "year",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Year Name",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "year",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "description": "For e.g. 2012, 2012-13",
+ "fieldname": "year",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Year Name",
+ "oldfieldname": "year",
+ "oldfieldtype": "Data",
+ "reqd": 1,
+ "unique": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "disabled",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Disabled",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "disabled",
+ "fieldtype": "Check",
+ "label": "Disabled"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "year_start_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Year Start Date",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "year_start_date",
- "oldfieldtype": "Date",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "year_start_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Year Start Date",
+ "no_copy": 1,
+ "oldfieldname": "year_start_date",
+ "oldfieldtype": "Date",
+ "reqd": 1,
+ "set_only_once": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "year_end_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Year End Date",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "year_end_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Year End Date",
+ "no_copy": 1,
+ "reqd": 1,
+ "set_only_once": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "companies",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Companies",
- "length": 0,
- "no_copy": 0,
- "options": "Fiscal Year Company",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "companies",
+ "fieldtype": "Table",
+ "label": "Companies",
+ "options": "Fiscal Year Company"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "fieldname": "auto_created",
- "fieldtype": "Check",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Auto Created",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "default": "0",
+ "fieldname": "auto_created",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Auto Created",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "description": "Less than 12 months.",
+ "fieldname": "is_short_year",
+ "fieldtype": "Check",
+ "label": "Is Short Year",
+ "set_only_once": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "fa fa-calendar",
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-04-25 14:21:41.273354",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Fiscal Year",
- "owner": "Administrator",
+ ],
+ "icon": "fa fa-calendar",
+ "idx": 1,
+ "links": [],
+ "modified": "2020-11-05 12:16:53.081573",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Fiscal Year",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Sales User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "read": 1,
+ "role": "Sales User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Purchase User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "read": 1,
+ "role": "Purchase User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Accounts User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "read": 1,
+ "role": "Accounts User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Stock User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "read": 1,
+ "role": "Stock User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Employee",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
+ "read": 1,
+ "role": "Employee"
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 1,
- "sort_field": "name",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0
+ ],
+ "show_name_in_global_search": 1,
+ "sort_field": "name",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
index d80bc7fad1..da6a3fd2ef 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
@@ -36,6 +36,11 @@ class FiscalYear(Document):
frappe.throw(_("Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."))
def validate_dates(self):
+ if self.is_short_year:
+ # Fiscal Year can be shorter than one year, in some jurisdictions
+ # under certain circumstances. For example, in the USA and Germany.
+ return
+
if getdate(self.year_start_date) > getdate(self.year_end_date):
frappe.throw(_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
FiscalYearIncorrectDate)
@@ -116,12 +121,8 @@ def auto_create_fiscal_year():
pass
def get_from_and_to_date(fiscal_year):
- from_and_to_date_tuple = frappe.db.sql("""select year_start_date, year_end_date
- from `tabFiscal Year` where name=%s""", (fiscal_year))[0]
-
- from_and_to_date = {
- "from_date": from_and_to_date_tuple[0],
- "to_date": from_and_to_date_tuple[1]
- }
-
- return from_and_to_date
+ fields = [
+ "year_start_date as from_date",
+ "year_end_date as to_date"
+ ]
+ return frappe.db.get_value("Fiscal Year", fiscal_year, fields, as_dict=1)
diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
index f7b7782766..cec4f4492d 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
@@ -11,6 +11,7 @@ test_records = frappe.get_test_records('Fiscal Year')
test_ignore = ["Company"]
class TestFiscalYear(unittest.TestCase):
+
def test_extra_year(self):
if frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2000"):
frappe.delete_doc("Fiscal Year", "_Test Fiscal Year 2000")
diff --git a/erpnext/accounts/doctype/fiscal_year/test_records.json b/erpnext/accounts/doctype/fiscal_year/test_records.json
index d5723ca62b..44052535cb 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_records.json
+++ b/erpnext/accounts/doctype/fiscal_year/test_records.json
@@ -1,4 +1,11 @@
[
+ {
+ "doctype": "Fiscal Year",
+ "year": "_Test Short Fiscal Year 2011",
+ "is_short_year": 1,
+ "year_end_date": "2011-04-01",
+ "year_start_date": "2011-12-31"
+ },
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2012",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index 4573c50134..b7bbb74ce9 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-03-25 10:53:52",
@@ -503,7 +504,7 @@
"idx": 176,
"is_submittable": 1,
"links": [],
- "modified": "2020-06-02 18:15:46.955697",
+ "modified": "2020-10-30 13:56:01.121995",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index 72149a665d..2e1f201e25 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2016-06-01 14:38:51.012597",
@@ -587,7 +588,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-09-02 13:39:43.383705",
+ "modified": "2020-10-30 13:56:20.007336",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 1cff3c661d..5bc57b4a84 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2020-01-24 15:29:29.933693",
@@ -1580,7 +1581,7 @@
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
- "modified": "2020-09-28 16:51:24.641755",
+ "modified": "2020-10-30 13:56:51.056083",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index a7e20a0c32..d486ff6028 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -39,6 +39,7 @@ class POSInvoice(SalesInvoice):
self.validate_serialised_or_batched_item()
self.validate_stock_availablility()
self.validate_return_items_qty()
+ self.validate_non_stock_items()
self.set_status()
self.set_account_for_mode_of_payment()
self.validate_pos()
@@ -174,6 +175,14 @@ class POSInvoice(SalesInvoice):
_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
.format(d.idx, bold_serial_no, bold_return_against)
)
+
+ def validate_non_stock_items(self):
+ for d in self.get("items"):
+ is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
+ if not is_stock_item:
+ frappe.throw(_("Row #{}: Item {} is a non stock item. You can only include stock items in a POS Invoice. ").format(
+ d.idx, frappe.bold(d.item_code)
+ ), title=_("Invalid Item"))
def validate_mode_of_payment(self):
if len(self.payments) == 0:
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 149c47673c..55a5b0e513 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -352,8 +352,14 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
pricing_rule_rate = 0.0
if pricing_rule.currency == args.currency:
pricing_rule_rate = pricing_rule.rate
+
+ if pricing_rule_rate:
+ # Override already set price list rate (from item price)
+ # if pricing_rule_rate > 0
+ item_details.update({
+ "price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
+ })
item_details.update({
- "price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
"discount_percentage": 0.0
})
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 22a031c162..ec0a485bfc 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -484,6 +484,43 @@ class TestPricingRule(unittest.TestCase):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
+ def test_item_price_with_pricing_rule(self):
+ item = make_item("Water Flask")
+ make_item_price("Water Flask", "_Test Price List", 100)
+
+ pricing_rule_record = {
+ "doctype": "Pricing Rule",
+ "title": "_Test Water Flask Rule",
+ "apply_on": "Item Code",
+ "items": [{
+ "item_code": "Water Flask",
+ }],
+ "selling": 1,
+ "currency": "INR",
+ "rate_or_discount": "Rate",
+ "rate": 0,
+ "margin_type": "Percentage",
+ "margin_rate_or_amount": 2,
+ "company": "_Test Company"
+ }
+ rule = frappe.get_doc(pricing_rule_record)
+ rule.insert()
+
+ si = create_sales_invoice(do_not_save=True, item_code="Water Flask")
+ si.selling_price_list = "_Test Price List"
+ si.save()
+
+ # If rate in Rule is 0, give preference to Item Price if it exists
+ self.assertEqual(si.items[0].price_list_rate, 100)
+ self.assertEqual(si.items[0].margin_rate_or_amount, 2)
+ self.assertEqual(si.items[0].rate_with_margin, 102)
+ self.assertEqual(si.items[0].rate, 102)
+
+ si.delete()
+ rule.delete()
+ frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
+ item.delete()
+
def make_pricing_rule(**args):
args = frappe._dict(args)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 8925b87b52..2df77a84c7 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-21 16:16:39",
@@ -1334,7 +1335,8 @@
"icon": "fa fa-file-text",
"idx": 204,
"is_submittable": 1,
- "modified": "2020-09-21 12:22:09.164068",
+ "links": [],
+ "modified": "2020-10-30 13:57:18.266978",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
@@ -1396,4 +1398,4 @@
"timeline_field": "supplier",
"title_field": "title",
"track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 2e5a7142a3..f2499d24b5 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -998,7 +998,7 @@ def make_purchase_invoice(**args):
'expense_account': args.expense_account or '_Test Account Cost for Goods Sold - _TC',
"conversion_factor": 1.0,
"serial_no": args.serial_no,
- "stock_uom": "_Test UOM",
+ "stock_uom": args.uom or "_Test UOM",
"cost_center": args.cost_center or "_Test Cost Center - _TC",
"project": args.project,
"rejected_warehouse": args.rejected_warehouse or "",
@@ -1040,7 +1040,8 @@ def make_purchase_invoice_against_cost_center(**args):
pi.is_return = args.is_return
pi.credit_to = args.return_against or "Creditors - _TC"
pi.is_subcontracted = args.is_subcontracted or "No"
- pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
+ if args.supplier_warehouse:
+ pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
pi.append("items", {
"item_code": args.item or args.item_code or "_Test Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index ae40153890..17fbe2def9 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-24 19:29:05",
@@ -1955,7 +1956,7 @@
"idx": 181,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-09 15:59:57.544736",
+ "modified": "2020-10-30 13:57:45.086303",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 4b598877d9..af6c6968dc 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1401,6 +1401,7 @@ def make_delivery_note(source_name, target_doc=None):
def set_missing_values(source, target):
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
+ target.run_method("set_po_nos")
target.run_method("calculate_taxes_and_totals")
def update_item(source_doc, target_doc, source_parent):
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 8b5e68b359..32ad4cb03a 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -140,9 +140,9 @@ def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_detai
else:
tds_amount = _get_tds(net_total, tax_details.rate)
else:
- supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
- fields = ['sum(net_amount)'],
- filters = {'parent': ('in', vouchers), 'docstatus': 1}, as_list=1)
+ supplier_credit_amount = frappe.get_all('Purchase Invoice',
+ fields = ['sum(net_total)'],
+ filters = {'name': ('in', vouchers), 'docstatus': 1, "apply_tds": 1}, as_list=1)
supplier_credit_amount = (supplier_credit_amount[0][0]
if supplier_credit_amount and supplier_credit_amount[0][0] else 0)
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index b1468999fc..a0b0cbb995 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -101,6 +101,29 @@ class TestTaxWithholdingCategory(unittest.TestCase):
for d in invoices:
d.cancel()
+ def test_single_threshold_tds_with_previous_vouchers_and_no_tds(self):
+ invoices = []
+ frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS")
+ pi = create_purchase_invoice(supplier="Test TDS Supplier2")
+ pi.submit()
+ invoices.append(pi)
+
+ # TDS not applied
+ pi = create_purchase_invoice(supplier="Test TDS Supplier2", do_not_apply_tds=True)
+ pi.submit()
+ invoices.append(pi)
+
+ pi = create_purchase_invoice(supplier="Test TDS Supplier2")
+ pi.submit()
+ invoices.append(pi)
+
+ self.assertEqual(pi.taxes_and_charges_deducted, 2000)
+ self.assertEqual(pi.grand_total, 8000)
+
+ # delete invoices to avoid clashing
+ for d in invoices:
+ d.cancel()
+
def create_purchase_invoice(**args):
# return sales invoice doc object
item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
@@ -109,7 +132,7 @@ def create_purchase_invoice(**args):
pi = frappe.get_doc({
"doctype": "Purchase Invoice",
"posting_date": today(),
- "apply_tds": 1,
+ "apply_tds": 0 if args.do_not_apply_tds else 1,
"supplier": args.supplier,
"company": '_Test Company',
"taxes_and_charges": "",
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index e8d3cd322b..64268b8064 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -59,7 +59,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
billing_address=party_address, shipping_address=shipping_address)
if fetch_payment_terms_template:
- party_details["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
+ party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company)
if not party_details.get("currency"):
party_details["currency"] = currency
@@ -315,7 +315,7 @@ def get_due_date(posting_date, party_type, party, company=None, bill_date=None):
due_date = None
if (bill_date or posting_date) and party:
due_date = bill_date or posting_date
- template_name = get_pyt_term_template(party, party_type, company)
+ template_name = get_payment_terms_template(party, party_type, company)
if template_name:
due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
@@ -422,7 +422,7 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
@frappe.whitelist()
-def get_pyt_term_template(party_name, party_type, company=None):
+def get_payment_terms_template(party_name, party_type, company=None):
if party_type not in ("Customer", "Supplier"):
return
template = None
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 044fc1d3ab..51fc7ec49a 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -160,6 +160,8 @@ class ReceivablePayableReport(object):
else:
# advance / unlinked payment or other adjustment
row.paid -= gle_balance
+ if gle.cost_center:
+ row.cost_center = str(gle.cost_center)
def update_sub_total_row(self, row, party):
total_row = self.total_row_map.get(party)
@@ -210,7 +212,6 @@ class ReceivablePayableReport(object):
for key, row in self.voucher_balance.items():
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
row.invoice_grand_total = row.invoiced
-
if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
# non-zero oustanding, we must consider this row
@@ -577,7 +578,7 @@ class ReceivablePayableReport(object):
self.gl_entries = frappe.db.sql("""
select
- name, posting_date, account, party_type, party, voucher_type, voucher_no,
+ name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center,
against_voucher_type, against_voucher, account_currency, remarks, {0}
from
`tabGL Entry`
@@ -741,6 +742,7 @@ class ReceivablePayableReport(object):
self.add_column(_("Customer Contact"), fieldname='customer_primary_contact',
fieldtype='Link', options='Contact')
+ self.add_column(label=_('Cost Center'), fieldname='cost_center', fieldtype='Data')
self.add_column(label=_('Voucher Type'), fieldname='voucher_type', fieldtype='Data')
self.add_column(label=_('Voucher No'), fieldname='voucher_no', fieldtype='Dynamic Link',
options='voucher_type', width=180)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 2f52a9e035..47483c9d1c 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -90,6 +90,11 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
this.frm.set_df_property("drop_ship", "hidden", !is_drop_ship);
if(doc.docstatus == 1) {
+ this.frm.fields_dict.items_section.wrapper.addClass("hide-border");
+ if(!this.frm.doc.set_warehouse) {
+ this.frm.fields_dict.items_section.wrapper.removeClass("hide-border");
+ }
+
if(!in_list(["Closed", "Delivered"], doc.status)) {
if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) {
this.frm.add_custom_button(__('Update Items'), () => {
@@ -126,16 +131,25 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
if(doc.status != "Closed") {
if (doc.status != "On Hold") {
if(flt(doc.per_received) < 100 && allow_receipt) {
- cur_frm.add_custom_button(__('Receipt'), this.make_purchase_receipt, __('Create'));
+ cur_frm.add_custom_button(__('Purchase Receipt'), this.make_purchase_receipt, __('Create'));
if(doc.is_subcontracted==="Yes" && me.has_unsupplied_items()) {
cur_frm.add_custom_button(__('Material to Supplier'),
function() { me.make_stock_entry(); }, __("Transfer"));
}
}
if(flt(doc.per_billed) < 100)
- cur_frm.add_custom_button(__('Invoice'),
+ cur_frm.add_custom_button(__('Purchase Invoice'),
this.make_purchase_invoice, __('Create'));
+ if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
+ cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create'));
+ }
+
+ if(flt(doc.per_billed)==0) {
+ this.frm.add_custom_button(__('Payment Request'),
+ function() { me.make_payment_request() }, __('Create'));
+ }
+
if(!doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name)
@@ -156,13 +170,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
});
}
}
- if(flt(doc.per_billed)==0) {
- this.frm.add_custom_button(__('Payment Request'),
- function() { me.make_payment_request() }, __('Create'));
- }
- if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
- cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create'));
- }
+
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
}
} else if(doc.docstatus===0) {
@@ -358,12 +366,16 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
source_doctype: "Material Request",
target: me.frm,
- setters: {},
+ setters: {
+ schedule_date: undefined,
+ status: undefined
+ },
get_query_filters: {
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99],
+ company: me.frm.doc.company
}
})
}, __("Get Items From"));
@@ -375,16 +387,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
source_doctype: "Supplier Quotation",
target: me.frm,
setters: {
- supplier: me.frm.doc.supplier
+ supplier: me.frm.doc.supplier,
+ valid_till: undefined
},
get_query_filters: {
docstatus: 1,
- status: ["!=", "Stopped"],
+ status: ["not in", ["Stopped", "Expired"]],
}
})
}, __("Get Items From"));
- this.frm.add_custom_button(__('Update rate as per last purchase'),
+ this.frm.add_custom_button(__('Update Rate as per Last Purchase'),
function() {
frappe.call({
"method": "get_last_purchase_rate",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 2747c7c54d..71231f68c8 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-21 16:16:39",
@@ -30,8 +31,8 @@
"customer_contact_email",
"section_addresses",
"supplier_address",
- "contact_person",
"address_display",
+ "contact_person",
"contact_display",
"contact_mobile",
"contact_email",
@@ -49,12 +50,14 @@
"plc_conversion_rate",
"ignore_pricing_rule",
"sec_warehouse",
- "set_warehouse",
- "col_break_warehouse",
"is_subcontracted",
+ "col_break_warehouse",
"supplier_warehouse",
- "items_section",
+ "before_items_section",
"scan_barcode",
+ "items_col_break",
+ "set_warehouse",
+ "items_section",
"items",
"sb_last_purchase",
"total_qty",
@@ -108,18 +111,13 @@
"payment_terms_template",
"payment_schedule",
"tracking_section",
- "per_billed",
+ "status",
"column_break_75",
+ "per_billed",
"per_received",
"terms_section_break",
"tc_name",
"terms",
- "more_info",
- "status",
- "ref_sq",
- "column_break_74",
- "party_account_currency",
- "inter_company_order_reference",
"column_break5",
"letter_head",
"select_print_heading",
@@ -131,7 +129,12 @@
"to_date",
"column_break_97",
"auto_repeat",
- "update_auto_repeat_reference"
+ "update_auto_repeat_reference",
+ "more_info",
+ "ref_sq",
+ "column_break_74",
+ "party_account_currency",
+ "inter_company_order_reference"
],
"fields": [
{
@@ -313,34 +316,34 @@
{
"fieldname": "supplier_address",
"fieldtype": "Link",
- "label": "Select Supplier Address",
+ "label": "Supplier Address",
"options": "Address",
"print_hide": 1
},
{
"fieldname": "contact_person",
"fieldtype": "Link",
- "label": "Contact Person",
+ "label": "Supplier Contact",
"options": "Contact",
"print_hide": 1
},
{
"fieldname": "address_display",
"fieldtype": "Small Text",
- "label": "Address",
+ "label": "Supplier Address Details",
"read_only": 1
},
{
"fieldname": "contact_display",
"fieldtype": "Small Text",
"in_global_search": 1,
- "label": "Contact",
+ "label": "Contact Name",
"read_only": 1
},
{
"fieldname": "contact_mobile",
"fieldtype": "Small Text",
- "label": "Mobile No",
+ "label": "Contact Mobile No",
"read_only": 1
},
{
@@ -358,14 +361,14 @@
{
"fieldname": "shipping_address",
"fieldtype": "Link",
- "label": "Select Shipping Address",
+ "label": "Company Shipping Address",
"options": "Address",
"print_hide": 1
},
{
"fieldname": "shipping_address_display",
"fieldtype": "Small Text",
- "label": "Shipping Address",
+ "label": "Shipping Address Details",
"print_hide": 1,
"read_only": 1
},
@@ -433,7 +436,8 @@
},
{
"fieldname": "sec_warehouse",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Subcontracting"
},
{
"description": "Sets 'Warehouse' in each row of the Items table.",
@@ -466,6 +470,7 @@
{
"fieldname": "items_section",
"fieldtype": "Section Break",
+ "hide_border": 1,
"oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart"
},
@@ -598,7 +603,8 @@
},
{
"fieldname": "section_break_52",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "hide_border": 1
},
{
"fieldname": "taxes",
@@ -626,10 +632,12 @@
{
"fieldname": "totals",
"fieldtype": "Section Break",
+ "label": "Taxes and Charges",
"oldfieldtype": "Section Break",
"options": "fa fa-money"
},
{
+ "depends_on": "base_taxes_and_charges_added",
"fieldname": "base_taxes_and_charges_added",
"fieldtype": "Currency",
"label": "Taxes and Charges Added (Company Currency)",
@@ -640,6 +648,7 @@
"read_only": 1
},
{
+ "depends_on": "base_taxes_and_charges_deducted",
"fieldname": "base_taxes_and_charges_deducted",
"fieldtype": "Currency",
"label": "Taxes and Charges Deducted (Company Currency)",
@@ -650,6 +659,7 @@
"read_only": 1
},
{
+ "depends_on": "base_total_taxes_and_charges",
"fieldname": "base_total_taxes_and_charges",
"fieldtype": "Currency",
"label": "Total Taxes and Charges (Company Currency)",
@@ -665,6 +675,7 @@
"fieldtype": "Column Break"
},
{
+ "depends_on": "taxes_and_charges_added",
"fieldname": "taxes_and_charges_added",
"fieldtype": "Currency",
"label": "Taxes and Charges Added",
@@ -675,6 +686,7 @@
"read_only": 1
},
{
+ "depends_on": "taxes_and_charges_deducted",
"fieldname": "taxes_and_charges_deducted",
"fieldtype": "Currency",
"label": "Taxes and Charges Deducted",
@@ -685,6 +697,7 @@
"read_only": 1
},
{
+ "depends_on": "total_taxes_and_charges",
"fieldname": "total_taxes_and_charges",
"fieldtype": "Currency",
"label": "Total Taxes and Charges",
@@ -694,7 +707,7 @@
},
{
"collapsible": 1,
- "collapsible_depends_on": "discount_amount",
+ "collapsible_depends_on": "apply_discount_on",
"fieldname": "discount_section",
"fieldtype": "Section Break",
"label": "Additional Discount"
@@ -734,7 +747,8 @@
},
{
"fieldname": "totals_section",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Totals"
},
{
"fieldname": "base_grand_total",
@@ -902,12 +916,12 @@
},
{
"fieldname": "ref_sq",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Ref SQ",
+ "fieldtype": "Link",
+ "label": "Supplier Quotation",
"no_copy": 1,
"oldfieldname": "ref_sq",
"oldfieldtype": "Data",
+ "options": "Supplier Quotation",
"print_hide": 1,
"read_only": 1
},
@@ -1061,7 +1075,7 @@
"collapsible": 1,
"fieldname": "tracking_section",
"fieldtype": "Section Break",
- "label": "Tracking"
+ "label": "Order Status"
},
{
"fieldname": "column_break_75",
@@ -1070,21 +1084,29 @@
{
"fieldname": "billing_address",
"fieldtype": "Link",
- "label": "Select Billing Address",
+ "label": "Company Billing Address",
"options": "Address"
},
{
"fieldname": "billing_address_display",
"fieldtype": "Small Text",
- "label": "Billing Address",
+ "label": "Billing Address Details",
"read_only": 1
+ },
+ {
+ "fieldname": "before_items_section",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "items_col_break",
+ "fieldtype": "Column Break"
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-07 14:31:57.661221",
+ "modified": "2020-10-30 13:58:14.697921",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 7a52c28a0e..10db240a44 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -24,6 +24,7 @@
"col_break2",
"uom",
"conversion_factor",
+ "stock_qty",
"sec_break1",
"price_list_rate",
"discount_percentage",
@@ -46,11 +47,8 @@
"column_break_32",
"base_net_rate",
"base_net_amount",
- "billed_amt",
"warehouse_and_reference",
"warehouse",
- "delivered_by_supplier",
- "project",
"material_request",
"material_request_item",
"sales_order",
@@ -58,36 +56,37 @@
"supplier_quotation",
"supplier_quotation_item",
"col_break5",
+ "delivered_by_supplier",
"against_blanket_order",
"blanket_order",
"blanket_order_rate",
"item_group",
"brand",
- "bom",
- "include_exploded_items",
"section_break_56",
- "stock_qty",
- "column_break_60",
"received_qty",
"returned_qty",
- "manufacture_details",
- "manufacturer",
- "column_break_14",
- "manufacturer_part_no",
- "more_info_section_break",
- "is_fixed_asset",
- "item_tax_rate",
+ "column_break_60",
+ "billed_amt",
"accounting_details",
"expense_account",
- "column_break_68",
+ "manufacture_details",
+ "manufacturer",
+ "manufacturer_part_no",
+ "column_break_14",
+ "bom",
+ "include_exploded_items",
"item_weight_details",
"weight_per_unit",
"total_weight",
"column_break_40",
"weight_uom",
"accounting_dimensions_section",
- "cost_center",
+ "project",
"dimension_col_break",
+ "cost_center",
+ "more_info_section_break",
+ "is_fixed_asset",
+ "item_tax_rate",
"section_break_72",
"page_break"
],
@@ -346,6 +345,7 @@
},
{
"default": "0",
+ "depends_on": "is_free_item",
"fieldname": "is_free_item",
"fieldtype": "Check",
"label": "Is Free Item",
@@ -508,9 +508,10 @@
},
{
"default": "0",
+ "depends_on": "delivered_by_supplier",
"fieldname": "delivered_by_supplier",
"fieldtype": "Check",
- "label": "To be delivered to customer",
+ "label": "To be Delivered to Customer",
"print_hide": 1,
"read_only": 1
},
@@ -558,6 +559,7 @@
"read_only": 1
},
{
+ "depends_on": "eval:parent.is_subcontracted == 'Yes'",
"fieldname": "bom",
"fieldtype": "Link",
"label": "BOM",
@@ -574,21 +576,21 @@
},
{
"fieldname": "section_break_56",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Billed, Received & Returned"
},
{
"fieldname": "stock_qty",
"fieldtype": "Float",
- "label": "Qty as per Stock UOM",
+ "label": "Qty in Stock UOM",
"no_copy": 1,
- "oldfieldname": "stock_qty",
- "oldfieldtype": "Currency",
"print_hide": 1,
"print_width": "100px",
"read_only": 1,
"width": "100px"
},
{
+ "depends_on": "received_qty",
"fieldname": "received_qty",
"fieldtype": "Float",
"label": "Received Qty",
@@ -612,9 +614,10 @@
"fieldtype": "Column Break"
},
{
+ "depends_on": "billed_amt",
"fieldname": "billed_amt",
"fieldtype": "Currency",
- "label": "Billed Amt",
+ "label": "Billed Amount",
"no_copy": 1,
"options": "currency",
"print_hide": 1,
@@ -633,6 +636,7 @@
"report_hide": 1
},
{
+ "collapsible": 1,
"fieldname": "accounting_details",
"fieldtype": "Section Break",
"label": "Accounting Details"
@@ -644,10 +648,6 @@
"options": "Account",
"print_hide": 1
},
- {
- "fieldname": "column_break_68",
- "fieldtype": "Column Break"
- },
{
"fieldname": "cost_center",
"fieldtype": "Link",
@@ -715,6 +715,7 @@
},
{
"default": "0",
+ "depends_on": "is_fixed_asset",
"fetch_from": "item_code.is_fixed_asset",
"fieldname": "is_fixed_asset",
"fieldtype": "Check",
@@ -728,9 +729,10 @@
}
],
"idx": 1,
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-21 11:55:58.643393",
+ "modified": "2020-10-30 11:59:47.670951",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 9a092ca5c3..b39c989073 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-21 16:16:45",
@@ -807,7 +808,7 @@
"idx": 29,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-01 20:56:17.932007",
+ "modified": "2020-10-30 13:58:33.043971",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",
diff --git a/erpnext/communication/doctype/communication_medium/communication_medium.json b/erpnext/communication/doctype/communication_medium/communication_medium.json
index f009b38877..1e1fe3bf49 100644
--- a/erpnext/communication/doctype/communication_medium/communication_medium.json
+++ b/erpnext/communication/doctype/communication_medium/communication_medium.json
@@ -1,12 +1,14 @@
{
+ "actions": [],
"autoname": "Prompt",
"creation": "2019-06-05 11:48:30.572795",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
+ "communication_channel",
"communication_medium_type",
- "catch_all",
"column_break_3",
+ "catch_all",
"provider",
"disabled",
"timeslots_section",
@@ -54,9 +56,16 @@
"fieldtype": "Table",
"label": "Timeslots",
"options": "Communication Medium Timeslot"
+ },
+ {
+ "fieldname": "communication_channel",
+ "fieldtype": "Select",
+ "label": "Communication Channel",
+ "options": "\nExotel"
}
],
- "modified": "2019-06-05 11:49:30.769006",
+ "links": [],
+ "modified": "2020-10-27 16:22:08.068542",
"modified_by": "Administrator",
"module": "Communication",
"name": "Communication Medium",
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 58861715c2..7504746e07 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -371,13 +371,27 @@ class SellingController(StockController):
self.make_sl_entries(sl_entries)
def set_po_nos(self):
- if self.doctype in ("Delivery Note", "Sales Invoice") and hasattr(self, "items"):
- ref_fieldname = "against_sales_order" if self.doctype == "Delivery Note" else "sales_order"
- sales_orders = list(set([d.get(ref_fieldname) for d in self.items if d.get(ref_fieldname)]))
- if sales_orders:
- po_nos = frappe.get_all('Sales Order', 'po_no', filters = {'name': ('in', sales_orders)})
- if po_nos and po_nos[0].get('po_no'):
- self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no])))
+ if self.doctype == 'Sales Invoice' and hasattr(self, "items"):
+ self.set_pos_for_sales_invoice()
+ if self.doctype == 'Delivery Note' and hasattr(self, "items"):
+ self.set_pos_for_delivery_note()
+
+ def set_pos_for_sales_invoice(self):
+ po_nos = []
+ self.get_po_nos('Sales Order', 'sales_order', po_nos)
+ self.get_po_nos('Delivery Note', 'delivery_note', po_nos)
+ self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(','))))
+
+ def set_pos_for_delivery_note(self):
+ po_nos = []
+ self.get_po_nos('Sales Order', 'against_sales_order', po_nos)
+ self.get_po_nos('Sales Invoice', 'against_sales_invoice', po_nos)
+ self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(','))))
+
+ def get_po_nos(self, ref_doctype, ref_fieldname, po_nos):
+ doc_list = list(set([d.get(ref_fieldname) for d in self.items if d.get(ref_fieldname)]))
+ if doc_list:
+ po_nos += [d.po_no for d in frappe.get_all(ref_doctype, 'po_no', filters = {'name': ('in', doc_list)}) if d.get('po_no')]
def set_gross_profit(self):
if self.doctype in ["Sales Order", "Quotation"]:
diff --git a/erpnext/crm/doctype/contract_template/contract_template.json b/erpnext/crm/doctype/contract_template/contract_template.json
index ef9974f863..5e4582f8d3 100644
--- a/erpnext/crm/doctype/contract_template/contract_template.json
+++ b/erpnext/crm/doctype/contract_template/contract_template.json
@@ -23,8 +23,7 @@
{
"fieldname": "contract_terms",
"fieldtype": "Text Editor",
- "label": "Contract Terms and Conditions",
- "read_only": 1
+ "label": "Contract Terms and Conditions"
},
{
"fieldname": "sb_fulfilment",
@@ -45,7 +44,7 @@
}
],
"links": [],
- "modified": "2020-06-03 00:24:58.179816",
+ "modified": "2020-11-11 17:49:44.879363",
"modified_by": "Administrator",
"module": "CRM",
"name": "Contract Template",
diff --git a/erpnext/demo/data/asset.json b/erpnext/demo/data/asset.json
index 23029ca5e3..44db2ae9e1 100644
--- a/erpnext/demo/data/asset.json
+++ b/erpnext/demo/data/asset.json
@@ -4,48 +4,55 @@
"item_code": "Computer",
"gross_purchase_amount": 100000,
"asset_owner": "Company",
- "available_for_use_date": "2017-01-02"
+ "available_for_use_date": "2017-01-02",
+ "location": "Main Location"
},
{
"asset_name": "Macbook Air - 1",
"item_code": "Computer",
"gross_purchase_amount": 60000,
"asset_owner": "Company",
- "available_for_use_date": "2017-10-02"
+ "available_for_use_date": "2017-10-02",
+ "location": "Avg Location"
},
{
"asset_name": "Conferrence Table",
"item_code": "Table",
"gross_purchase_amount": 30000,
"asset_owner": "Company",
- "available_for_use_date": "2018-10-02"
+ "available_for_use_date": "2018-10-02",
+ "location": "Zany Location"
},
{
"asset_name": "Lunch Table",
"item_code": "Table",
"gross_purchase_amount": 20000,
"asset_owner": "Company",
- "available_for_use_date": "2018-06-02"
+ "available_for_use_date": "2018-06-02",
+ "location": "Fletcher Location"
},
{
"asset_name": "ERPNext",
"item_code": "ERP",
"gross_purchase_amount": 100000,
"asset_owner": "Company",
- "available_for_use_date": "2018-09-02"
+ "available_for_use_date": "2018-09-02",
+ "location":"Main Location"
},
{
"asset_name": "Chair 1",
"item_code": "Chair",
"gross_purchase_amount": 10000,
"asset_owner": "Company",
- "available_for_use_date": "2018-07-02"
+ "available_for_use_date": "2018-07-02",
+ "location": "Zany Location"
},
{
"asset_name": "Chair 2",
"item_code": "Chair",
"gross_purchase_amount": 10000,
"asset_owner": "Company",
- "available_for_use_date": "2018-07-02"
+ "available_for_use_date": "2018-07-02",
+ "location": "Avg Location"
}
]
diff --git a/erpnext/demo/data/location.json b/erpnext/demo/data/location.json
new file mode 100644
index 0000000000..b521aa08c4
--- /dev/null
+++ b/erpnext/demo/data/location.json
@@ -0,0 +1,22 @@
+[
+ {
+ "location_name": "Main Location",
+ "latitude": 40.0,
+ "longitude": 20.0
+ },
+ {
+ "location_name": "Avg Location",
+ "latitude": 63.0,
+ "longitude": 99.3
+ },
+ {
+ "location_name": "Zany Location",
+ "latitude": 47.5,
+ "longitude": 10.0
+ },
+ {
+ "location_name": "Fletcher Location",
+ "latitude": 100.90,
+ "longitude": 80
+ }
+]
\ No newline at end of file
diff --git a/erpnext/demo/setup/manufacture.py b/erpnext/demo/setup/manufacture.py
index d3846369cd..7d6b5012ea 100644
--- a/erpnext/demo/setup/manufacture.py
+++ b/erpnext/demo/setup/manufacture.py
@@ -9,6 +9,7 @@ from erpnext.demo.domains import data
from six import iteritems
def setup_data():
+ import_json("Location")
import_json("Asset Category")
setup_item()
setup_workstation()
diff --git a/erpnext/demo/user/stock.py b/erpnext/demo/user/stock.py
index f95a6b8331..d44da7d127 100644
--- a/erpnext/demo/user/stock.py
+++ b/erpnext/demo/user/stock.py
@@ -79,7 +79,7 @@ def make_stock_reconciliation():
if item.qty:
item.qty = item.qty - round(random.randint(1, item.qty))
try:
- stock_reco.insert(ignore_permissions=True)
+ stock_reco.insert(ignore_permissions=True, ignore_mandatory=True)
stock_reco.submit()
frappe.db.commit()
except OpeningEntryAccountError:
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.py b/erpnext/education/doctype/student_attendance/student_attendance.py
index 72a8f55c66..2e9e6cf8d6 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance.py
+++ b/erpnext/education/doctype/student_attendance/student_attendance.py
@@ -6,8 +6,10 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
-from frappe.utils import get_link_to_form, getdate
+from frappe.utils import get_link_to_form, getdate, formatdate
+from erpnext import get_default_company
from erpnext.education.api import get_student_group_students
+from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
class StudentAttendance(Document):
def validate(self):
@@ -17,6 +19,7 @@ class StudentAttendance(Document):
self.set_student_group()
self.validate_student()
self.validate_duplication()
+ self.validate_is_holiday()
def set_date(self):
if self.course_schedule:
@@ -78,3 +81,18 @@ class StudentAttendance(Document):
record = get_link_to_form('Student Attendance', attendance_record)
frappe.throw(_('Student Attendance record {0} already exists against the Student {1}')
.format(record, frappe.bold(self.student)), title=_('Duplicate Entry'))
+
+ def validate_is_holiday(self):
+ holiday_list = get_holiday_list()
+ if is_holiday(holiday_list, self.date):
+ frappe.throw(_('Attendance cannot be marked for {0} as it is a holiday.').format(
+ frappe.bold(formatdate(self.date))))
+
+def get_holiday_list(company=None):
+ if not company:
+ company = get_default_company() or frappe.get_all('Company')[0].name
+
+ holiday_list = frappe.get_cached_value('Company', company, 'default_holiday_list')
+ if not holiday_list:
+ frappe.throw(_('Please set a default Holiday List for Company {0}').format(frappe.bold(get_default_company())))
+ return holiday_list
diff --git a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
index be2644077a..028db91881 100644
--- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
+++ b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
@@ -20,10 +20,10 @@ def get_student_attendance_records(based_on, date=None, student_group=None, cour
student_list = frappe.get_list("Student Group Student", fields=["student", "student_name", "group_roll_number"] , \
filters={"parent": student_group, "active": 1}, order_by= "group_roll_number")
- if not student_list:
- student_list = frappe.get_list("Student Group Student", fields=["student", "student_name", "group_roll_number"] ,
+ if not student_list:
+ student_list = frappe.get_list("Student Group Student", fields=["student", "student_name", "group_roll_number"] ,
filters={"parent": student_group, "active": 1}, order_by= "group_roll_number")
-
+
if course_schedule:
student_attendance_list= frappe.db.sql('''select student, status from `tabStudent Attendance` where \
course_schedule= %s''', (course_schedule), as_dict=1)
@@ -32,7 +32,7 @@ def get_student_attendance_records(based_on, date=None, student_group=None, cour
student_group= %s and date= %s and \
(course_schedule is Null or course_schedule='')''',
(student_group, date), as_dict=1)
-
+
for attendance in student_attendance_list:
for student in student_list:
if student.student == attendance.student:
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application.json b/erpnext/education/doctype/student_leave_application/student_leave_application.json
index ad5397629b..31b3da2fbd 100644
--- a/erpnext/education/doctype/student_leave_application/student_leave_application.json
+++ b/erpnext/education/doctype/student_leave_application/student_leave_application.json
@@ -11,6 +11,7 @@
"column_break_3",
"from_date",
"to_date",
+ "total_leave_days",
"section_break_5",
"attendance_based_on",
"student_group",
@@ -110,11 +111,17 @@
{
"fieldname": "column_break_11",
"fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "total_leave_days",
+ "fieldtype": "Float",
+ "label": "Total Leave Days",
+ "read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-07-08 13:22:38.329002",
+ "modified": "2020-09-21 18:10:24.440669",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Leave Application",
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application.py b/erpnext/education/doctype/student_leave_application/student_leave_application.py
index c8841c999a..ef670124c3 100644
--- a/erpnext/education/doctype/student_leave_application/student_leave_application.py
+++ b/erpnext/education/doctype/student_leave_application/student_leave_application.py
@@ -6,11 +6,14 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from datetime import timedelta
-from frappe.utils import get_link_to_form, getdate
+from frappe.utils import get_link_to_form, getdate, date_diff, flt
+from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
+from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
from frappe.model.document import Document
class StudentLeaveApplication(Document):
def validate(self):
+ self.validate_holiday_list()
self.validate_duplicate()
self.validate_from_to_dates('from_date', 'to_date')
@@ -39,10 +42,19 @@ class StudentLeaveApplication(Document):
frappe.throw(_('Leave application {0} already exists against the student {1}')
.format(link, frappe.bold(self.student)), title=_('Duplicate Entry'))
+ def validate_holiday_list(self):
+ holiday_list = get_holiday_list()
+ self.total_leave_days = get_number_of_leave_days(self.from_date, self.to_date, holiday_list)
+
def update_attendance(self):
+ holiday_list = get_holiday_list()
+
for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
date = dt.strftime('%Y-%m-%d')
+ if is_holiday(holiday_list, date):
+ continue
+
attendance = frappe.db.exists('Student Attendance', {
'student': self.student,
'date': date,
@@ -89,3 +101,19 @@ class StudentLeaveApplication(Document):
def daterange(start_date, end_date):
for n in range(int ((end_date - start_date).days)+1):
yield start_date + timedelta(n)
+
+def get_number_of_leave_days(from_date, to_date, holiday_list):
+ number_of_days = date_diff(to_date, from_date) + 1
+
+ holidays = frappe.db.sql("""
+ SELECT
+ COUNT(DISTINCT holiday_date)
+ FROM `tabHoliday` h1,`tabHoliday List` h2
+ WHERE
+ h1.parent = h2.name and
+ h1.holiday_date between %s and %s and
+ h2.name = %s""", (from_date, to_date, holiday_list))[0][0]
+
+ number_of_days = flt(number_of_days) - flt(holidays)
+
+ return number_of_days
diff --git a/erpnext/education/doctype/student_leave_application/test_student_leave_application.py b/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
index e9b568ad70..fcdd42825f 100644
--- a/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
+++ b/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
@@ -5,13 +5,15 @@ from __future__ import unicode_literals
import frappe
import unittest
-from frappe.utils import getdate, add_days
+from frappe.utils import getdate, add_days, add_months
+from erpnext import get_default_company
from erpnext.education.doctype.student_group.test_student_group import get_random_group
from erpnext.education.doctype.student.test_student import create_student
class TestStudentLeaveApplication(unittest.TestCase):
def setUp(self):
frappe.db.sql("""delete from `tabStudent Leave Application`""")
+ create_holiday_list()
def test_attendance_record_creation(self):
leave_application = create_leave_application()
@@ -35,20 +37,45 @@ class TestStudentLeaveApplication(unittest.TestCase):
attendance_status = frappe.db.get_value('Student Attendance', {'leave_application': leave_application.name}, 'docstatus')
self.assertTrue(attendance_status, 2)
+ def test_holiday(self):
+ today = getdate()
+ leave_application = create_leave_application(from_date=today, to_date= add_days(today, 1), submit=0)
-def create_leave_application(from_date=None, to_date=None, mark_as_present=0):
+ # holiday list validation
+ company = get_default_company() or frappe.get_all('Company')[0].name
+ frappe.db.set_value('Company', company, 'default_holiday_list', '')
+ self.assertRaises(frappe.ValidationError, leave_application.save)
+
+ frappe.db.set_value('Company', company, 'default_holiday_list', 'Test Holiday List for Student')
+ leave_application.save()
+
+ leave_application.reload()
+ self.assertEqual(leave_application.total_leave_days, 1)
+
+ # check no attendance record created for a holiday
+ leave_application.submit()
+ self.assertIsNone(frappe.db.exists('Student Attendance', {'leave_application': leave_application.name, 'date': add_days(today, 1)}))
+
+ def tearDown(self):
+ company = get_default_company() or frappe.get_all('Company')[0].name
+ frappe.db.set_value('Company', company, 'default_holiday_list', '_Test Holiday List')
+
+
+def create_leave_application(from_date=None, to_date=None, mark_as_present=0, submit=1):
student = get_student()
- leave_application = frappe.get_doc({
- 'doctype': 'Student Leave Application',
- 'student': student.name,
- 'attendance_based_on': 'Student Group',
- 'student_group': get_random_group().name,
- 'from_date': from_date if from_date else getdate(),
- 'to_date': from_date if from_date else getdate(),
- 'mark_as_present': mark_as_present
- }).insert()
- leave_application.submit()
+ leave_application = frappe.new_doc('Student Leave Application')
+ leave_application.student = student.name
+ leave_application.attendance_based_on = 'Student Group'
+ leave_application.student_group = get_random_group().name
+ leave_application.from_date = from_date if from_date else getdate()
+ leave_application.to_date = from_date if from_date else getdate()
+ leave_application.mark_as_present = mark_as_present
+
+ if submit:
+ leave_application.insert()
+ leave_application.submit()
+
return leave_application
def create_student_attendance(date=None, status=None):
@@ -67,4 +94,22 @@ def get_student():
email='test_student@gmail.com',
first_name='Test',
last_name='Student'
- ))
\ No newline at end of file
+ ))
+
+def create_holiday_list():
+ holiday_list = 'Test Holiday List for Student'
+ today = getdate()
+ if not frappe.db.exists('Holiday List', holiday_list):
+ frappe.get_doc(dict(
+ doctype = 'Holiday List',
+ holiday_list_name = holiday_list,
+ from_date = add_months(today, -6),
+ to_date = add_months(today, 6),
+ holidays = [
+ dict(holiday_date=add_days(today, 1), description = 'Test')
+ ]
+ )).insert()
+
+ company = get_default_company() or frappe.get_all('Company')[0].name
+ frappe.db.set_value('Company', company, 'default_holiday_list', holiday_list)
+ return holiday_list
\ No newline at end of file
diff --git a/erpnext/education/report/absent_student_report/absent_student_report.py b/erpnext/education/report/absent_student_report/absent_student_report.py
index 4e57cc6c22..c3487ccaff 100644
--- a/erpnext/education/report/absent_student_report/absent_student_report.py
+++ b/erpnext/education/report/absent_student_report/absent_student_report.py
@@ -3,8 +3,10 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import cstr, cint, getdate
+from frappe.utils import formatdate
from frappe import msgprint, _
+from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
+from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
def execute(filters=None):
if not filters: filters = {}
@@ -15,6 +17,11 @@ def execute(filters=None):
columns = get_columns(filters)
date = filters.get("date")
+ holiday_list = get_holiday_list()
+ if is_holiday(holiday_list, filters.get("date")):
+ msgprint(_("No attendance has been marked for {0} as it is a Holiday").format(frappe.bold(formatdate(filters.get("date")))))
+
+
absent_students = get_absent_students(date)
leave_applicants = get_leave_applications(date)
if absent_students:
diff --git a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py
index c65d233ccc..7793dcf395 100644
--- a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py
+++ b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py
@@ -3,8 +3,10 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import cstr, cint, getdate
+from frappe.utils import formatdate
from frappe import msgprint, _
+from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
+from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
def execute(filters=None):
if not filters: filters = {}
@@ -12,6 +14,10 @@ def execute(filters=None):
if not filters.get("date"):
msgprint(_("Please select date"), raise_exception=1)
+ holiday_list = get_holiday_list()
+ if is_holiday(holiday_list, filters.get("date")):
+ msgprint(_("No attendance has been marked for {0} as it is a Holiday").format(frappe.bold(formatdate(filters.get("date")))))
+
columns = get_columns(filters)
active_student_group = get_active_student_group()
diff --git a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py
index d820bfbb21..04dc8c0e56 100644
--- a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py
+++ b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py
@@ -7,6 +7,8 @@ from frappe.utils import cstr, cint, getdate, get_first_day, get_last_day, date_
from frappe import msgprint, _
from calendar import monthrange
from erpnext.education.api import get_student_group_students
+from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
+from erpnext.support.doctype.issue.issue import get_holidays
def execute(filters=None):
if not filters: filters = {}
@@ -19,26 +21,32 @@ def execute(filters=None):
students_list = get_students_list(students)
att_map = get_attendance_list(from_date, to_date, filters.get("student_group"), students_list)
data = []
+
for stud in students:
row = [stud.student, stud.student_name]
student_status = frappe.db.get_value("Student", stud.student, "enabled")
date = from_date
total_p = total_a = 0.0
+
for day in range(total_days_in_month):
status="None"
+
if att_map.get(stud.student):
status = att_map.get(stud.student).get(date, "None")
elif not student_status:
status = "Inactive"
else:
status = "None"
- status_map = {"Present": "P", "Absent": "A", "None": "", "Inactive":"-"}
+
+ status_map = {"Present": "P", "Absent": "A", "None": "", "Inactive":"-", "Holiday":"H"}
row.append(status_map[status])
+
if status == "Present":
total_p += 1
elif status == "Absent":
total_a += 1
date = add_days(date, 1)
+
row += [total_p, total_a]
data.append(row)
return columns, data
@@ -63,14 +71,19 @@ def get_attendance_list(from_date, to_date, student_group, students_list):
and date between %s and %s
order by student, date''',
(student_group, from_date, to_date), as_dict=1)
+
att_map = {}
students_with_leave_application = get_students_with_leave_application(from_date, to_date, students_list)
for d in attendance_list:
att_map.setdefault(d.student, frappe._dict()).setdefault(d.date, "")
+
if students_with_leave_application.get(d.date) and d.student in students_with_leave_application.get(d.date):
att_map[d.student][d.date] = "Present"
else:
att_map[d.student][d.date] = d.status
+
+ att_map = mark_holidays(att_map, from_date, to_date, students_list)
+
return att_map
def get_students_with_leave_application(from_date, to_date, students_list):
@@ -108,3 +121,14 @@ def get_attendance_years():
if not year_list:
year_list = [getdate().year]
return "\n".join(str(year) for year in year_list)
+
+def mark_holidays(att_map, from_date, to_date, students_list):
+ holiday_list = get_holiday_list()
+ holidays = get_holidays(holiday_list)
+
+ for dt in daterange(getdate(from_date), getdate(to_date)):
+ if dt in holidays:
+ for student in students_list:
+ att_map.setdefault(student, frappe._dict()).setdefault(dt, "Holiday")
+
+ return att_map
diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py
index d59f909298..8aa7453bd6 100644
--- a/erpnext/erpnext_integrations/connectors/shopify_connection.py
+++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py
@@ -2,12 +2,13 @@ from __future__ import unicode_literals
import frappe
from frappe import _
import json
-from frappe.utils import cstr, cint, nowdate, flt
+from frappe.utils import cstr, cint, nowdate, getdate, flt, get_request_session, get_datetime
from erpnext.erpnext_integrations.utils import validate_webhooks_request
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import sync_item_from_shopify
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer
from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log, dump_request_data
+from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header
@frappe.whitelist(allow_guest=True)
@validate_webhooks_request("Shopify Settings", 'X-Shopify-Hmac-Sha256', secret_key='shared_secret')
@@ -18,7 +19,7 @@ def store_request_data(order=None, event=None):
dump_request_data(order, event)
-def sync_sales_order(order, request_id=None):
+def sync_sales_order(order, request_id=None, old_order_sync=False):
frappe.set_user('Administrator')
shopify_settings = frappe.get_doc("Shopify Settings")
frappe.flags.request_id = request_id
@@ -27,7 +28,7 @@ def sync_sales_order(order, request_id=None):
try:
validate_customer(order, shopify_settings)
validate_item(order, shopify_settings)
- create_order(order, shopify_settings)
+ create_order(order, shopify_settings, old_order_sync=old_order_sync)
except Exception as e:
make_shopify_log(status="Error", exception=e)
@@ -77,13 +78,13 @@ def validate_item(order, shopify_settings):
if item.get("product_id") and not frappe.db.get_value("Item", {"shopify_product_id": item.get("product_id")}, "name"):
sync_item_from_shopify(shopify_settings, item)
-def create_order(order, shopify_settings, company=None):
+def create_order(order, shopify_settings, old_order_sync=False, company=None):
so = create_sales_order(order, shopify_settings, company)
if so:
if order.get("financial_status") == "paid":
- create_sales_invoice(order, shopify_settings, so)
+ create_sales_invoice(order, shopify_settings, so, old_order_sync=old_order_sync)
- if order.get("fulfillments"):
+ if order.get("fulfillments") and not old_order_sync:
create_delivery_note(order, shopify_settings, so)
def create_sales_order(shopify_order, shopify_settings, company=None):
@@ -92,7 +93,7 @@ def create_sales_order(shopify_order, shopify_settings, company=None):
so = frappe.db.get_value("Sales Order", {"shopify_order_id": shopify_order.get("id")}, "name")
if not so:
- items = get_order_items(shopify_order.get("line_items"), shopify_settings)
+ items = get_order_items(shopify_order.get("line_items"), shopify_settings, getdate(shopify_order.get('created_at')))
if not items:
message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master'
@@ -106,8 +107,10 @@ def create_sales_order(shopify_order, shopify_settings, company=None):
"doctype": "Sales Order",
"naming_series": shopify_settings.sales_order_series or "SO-Shopify-",
"shopify_order_id": shopify_order.get("id"),
+ "shopify_order_number": shopify_order.get("name"),
"customer": customer or shopify_settings.default_customer,
- "delivery_date": nowdate(),
+ "transaction_date": getdate(shopify_order.get("created_at")) or nowdate(),
+ "delivery_date": getdate(shopify_order.get("created_at")) or nowdate(),
"company": shopify_settings.company,
"selling_price_list": shopify_settings.price_list,
"ignore_pricing_rule": 1,
@@ -132,12 +135,20 @@ def create_sales_order(shopify_order, shopify_settings, company=None):
frappe.db.commit()
return so
-def create_sales_invoice(shopify_order, shopify_settings, so):
+def create_sales_invoice(shopify_order, shopify_settings, so, old_order_sync=False):
if not frappe.db.get_value("Sales Invoice", {"shopify_order_id": shopify_order.get("id")}, "name")\
and so.docstatus==1 and not so.per_billed and cint(shopify_settings.sync_sales_invoice):
+ if old_order_sync:
+ posting_date = getdate(shopify_order.get('created_at'))
+ else:
+ posting_date = nowdate()
+
si = make_sales_invoice(so.name, ignore_permissions=True)
si.shopify_order_id = shopify_order.get("id")
+ si.shopify_order_number = shopify_order.get("name")
+ si.set_posting_time = 1
+ si.posting_date = posting_date
si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-"
si.flags.ignore_mandatory = True
set_cost_center(si.items, shopify_settings.cost_center)
@@ -169,6 +180,9 @@ def create_delivery_note(shopify_order, shopify_settings, so):
dn = make_delivery_note(so.name)
dn.shopify_order_id = fulfillment.get("order_id")
+ dn.shopify_order_number = shopify_order.get("name")
+ dn.set_posting_time = 1
+ dn.posting_date = getdate(fulfillment.get("created_at"))
dn.shopify_fulfillment_id = fulfillment.get("id")
dn.naming_series = shopify_settings.delivery_note_series or "DN-Shopify-"
dn.items = get_fulfillment_items(dn.items, fulfillment.get("line_items"), shopify_settings)
@@ -187,7 +201,7 @@ def get_discounted_amount(order):
discounted_amount += flt(discount.get("amount"))
return discounted_amount
-def get_order_items(order_items, shopify_settings):
+def get_order_items(order_items, shopify_settings, delivery_date):
items = []
all_product_exists = True
product_not_exists = []
@@ -205,7 +219,7 @@ def get_order_items(order_items, shopify_settings):
"item_code": item_code,
"item_name": shopify_item.get("name"),
"rate": shopify_item.get("price"),
- "delivery_date": nowdate(),
+ "delivery_date": delivery_date,
"qty": shopify_item.get("quantity"),
"stock_uom": shopify_item.get("uom") or _("Nos"),
"warehouse": shopify_settings.warehouse
@@ -265,3 +279,64 @@ def get_tax_account_head(tax):
frappe.throw(_("Tax Account not specified for Shopify Tax {0}").format(tax.get("title")))
return tax_account
+
+@frappe.whitelist(allow_guest=True)
+def sync_old_orders():
+ frappe.set_user('Administrator')
+ shopify_settings = frappe.get_doc('Shopify Settings')
+
+ if not shopify_settings.sync_missing_orders:
+ return
+
+ url = get_url(shopify_settings)
+ session = get_request_session()
+
+ try:
+ res = session.get(url, headers=get_header(shopify_settings))
+ res.raise_for_status()
+ orders = res.json()["orders"]
+
+ for order in orders:
+ if is_sync_complete(shopify_settings, order):
+ stop_sync(shopify_settings)
+ return
+
+ sync_sales_order(order=order, old_order_sync=True)
+ last_order_id = order.get('id')
+
+ if last_order_id:
+ shopify_settings.load_from_db()
+ shopify_settings.last_order_id = last_order_id
+ shopify_settings.save()
+ frappe.db.commit()
+
+ except Exception as e:
+ raise e
+
+def stop_sync(shopify_settings):
+ shopify_settings.sync_missing_orders = 0
+ shopify_settings.last_order_id = ''
+ shopify_settings.save()
+ frappe.db.commit()
+
+def get_url(shopify_settings):
+ last_order_id = shopify_settings.last_order_id
+
+ if not last_order_id:
+ if shopify_settings.sync_based_on == 'Date':
+ url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&created_at_min={0}&since_id=0".format(
+ get_datetime(shopify_settings.from_date)), shopify_settings)
+ else:
+ url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(
+ shopify_settings.from_order_id), shopify_settings)
+ else:
+ url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings)
+
+ return url
+
+def is_sync_complete(shopify_settings, order):
+ if shopify_settings.sync_based_on == 'Date':
+ return getdate(shopify_settings.to_date) < getdate(order.get('created_at'))
+ else:
+ return cstr(order.get('id')) == cstr(shopify_settings.to_order_id)
+
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
index 2e10751f96..20ec06373e 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
@@ -1,7 +1,9 @@
{
+ "actions": [],
"creation": "2015-05-18 05:21:07.270859",
"doctype": "DocType",
"document_type": "System",
+ "engine": "InnoDB",
"field_order": [
"status_html",
"enable_shopify",
@@ -40,7 +42,16 @@
"sales_invoice_series",
"section_break_22",
"html_16",
- "taxes"
+ "taxes",
+ "syncing_details_section",
+ "sync_missing_orders",
+ "sync_based_on",
+ "column_break_41",
+ "from_date",
+ "to_date",
+ "from_order_id",
+ "to_order_id",
+ "last_order_id"
],
"fields": [
{
@@ -255,10 +266,71 @@
"fieldtype": "Table",
"label": "Shopify Tax Account",
"options": "Shopify Tax Account"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "syncing_details_section",
+ "fieldtype": "Section Break",
+ "label": "Syncing Missing Orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_missing_orders",
+ "fieldname": "last_order_id",
+ "fieldtype": "Data",
+ "label": "Last Order Id",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_41",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "description": "On checking this Order from the ",
+ "fieldname": "sync_missing_orders",
+ "fieldtype": "Check",
+ "label": "Sync Missing Old Shopify Orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_missing_orders",
+ "fieldname": "sync_based_on",
+ "fieldtype": "Select",
+ "label": "Sync Based On",
+ "mandatory_depends_on": "eval:doc.sync_missing_orders",
+ "options": "\nDate\nShopify Order Id"
+ },
+ {
+ "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders",
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "label": "From Date",
+ "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders",
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "label": "To Date",
+ "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders",
+ "fieldname": "from_order_id",
+ "fieldtype": "Data",
+ "label": "From Order Id",
+ "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders",
+ "fieldname": "to_order_id",
+ "fieldtype": "Data",
+ "label": "To Order Id",
+ "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders"
}
],
"issingle": 1,
- "modified": "2020-09-18 17:26:09.703215",
+ "links": [],
+ "modified": "2020-11-05 20:44:03.664891",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Shopify Settings",
@@ -277,4 +349,4 @@
],
"sort_field": "modified",
"sort_order": "DESC"
-}
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
index 25ffd28109..cbdf90681d 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
@@ -87,7 +87,7 @@ def get_shopify_url(path, settings):
def get_header(settings):
header = {'Content-Type': 'application/json'}
- return header;
+ return header
@frappe.whitelist()
def get_series():
@@ -121,17 +121,23 @@ def setup_custom_fields():
],
"Sales Order": [
dict(fieldname='shopify_order_id', label='Shopify Order Id',
- fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
+ fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
+ dict(fieldname='shopify_order_number', label='Shopify Order Number',
+ fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1)
],
"Delivery Note":[
dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
+ dict(fieldname='shopify_order_number', label='Shopify Order Number',
+ fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1),
dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
],
"Sales Invoice": [
dict(fieldname='shopify_order_id', label='Shopify Order Id',
- fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
+ fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
+ dict(fieldname='shopify_order_number', label='Shopify Order Number',
+ fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1)
]
}
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
index 64ef3dc085..30fa23cfb4 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
@@ -58,7 +58,7 @@ class ShopifySettings(unittest.TestCase):
}).save(ignore_permissions=True)
self.shopify_settings = shopify_settings
-
+
def test_order(self):
### Create Customer ###
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_customer.json")) as shopify_customer:
@@ -75,7 +75,7 @@ class ShopifySettings(unittest.TestCase):
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_order.json")) as shopify_order:
shopify_order = json.load(shopify_order)
- create_order(shopify_order.get("order"), self.shopify_settings, "_Test Company")
+ create_order(shopify_order.get("order"), self.shopify_settings, False, company="_Test Company")
sales_order = frappe.get_doc("Sales Order", {"shopify_order_id": cstr(shopify_order.get("order").get("id"))})
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index dbb6c0d92e..b4c57d7c91 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -15,10 +15,10 @@ app_logo_url = '/assets/erpnext/images/erp-icon.svg'
develop_version = '13.x.x-develop'
-app_include_js = "assets/js/erpnext.min.js"
-app_include_css = "assets/css/erpnext.css"
-web_include_js = "assets/js/erpnext-web.min.js"
-web_include_css = "assets/css/erpnext-web.css"
+app_include_js = "/assets/js/erpnext.min.js"
+app_include_css = "/assets/css/erpnext.css"
+web_include_js = "/assets/js/erpnext-web.min.js"
+web_include_css = "/assets/css/erpnext-web.css"
doctype_js = {
"Address": "public/js/address.js",
@@ -250,7 +250,11 @@ doc_events = {
"on_trash": "erpnext.regional.check_deletion_permission"
},
"Purchase Invoice": {
- "validate": "erpnext.regional.india.utils.update_grand_total_for_rcm"
+ "validate": [
+ "erpnext.regional.india.utils.update_grand_total_for_rcm",
+ "erpnext.regional.united_arab_emirates.utils.update_grand_total_for_rcm",
+ "erpnext.regional.united_arab_emirates.utils.validate_returns"
+ ]
},
"Payment Entry": {
"on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status", "erpnext.accounts.doctype.dunning.dunning.resolve_dunning"],
@@ -307,6 +311,7 @@ scheduler_events = {
"erpnext.projects.doctype.project.project.collect_project_status",
"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
+ "erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
],
"daily": [
"erpnext.stock.reorder_item.reorder_item",
@@ -390,7 +395,8 @@ regional_overrides = {
'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries'
},
'United Arab Emirates': {
- 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data'
+ 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',
+ 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.united_arab_emirates.utils.make_regional_gl_entries',
},
'Saudi Arabia': {
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data'
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
index 8913c648c5..c1dcc97b1a 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -32,7 +32,7 @@ class LeaveEncashment(Document):
additional_salary.employee = self.employee
earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component")
if not earning_component:
- frappe.throw(_("Please set Earning Component for Leave type: {0}.".format(self.leave_type)))
+ frappe.throw(_("Please set Earning Component for Leave type: {0}.").format(self.leave_type))
additional_salary.salary_component = earning_component
additional_salary.payroll_date = self.encashment_date
additional_salary.amount = self.encashment_amount
@@ -98,7 +98,11 @@ class LeaveEncashment(Document):
create_leave_ledger_entry(self, args, submit)
# create reverse entry for expired leaves
- to_date = self.get_leave_allocation().get('to_date')
+ leave_allocation = self.get_leave_allocation()
+ if not leave_allocation:
+ return
+
+ to_date = leave_allocation.get('to_date')
if to_date < getdate(nowdate()):
args = frappe._dict(
leaves=self.encashable_days,
diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.js b/erpnext/hr/doctype/upload_attendance/upload_attendance.js
index 9df2948a15..29aa85484a 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.js
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.js
@@ -24,10 +24,10 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
}
window.location.href = repl(frappe.request.url +
'?cmd=%(cmd)s&from_date=%(from_date)s&to_date=%(to_date)s', {
- cmd: "erpnext.hr.doctype.upload_attendance.upload_attendance.get_template",
- from_date: this.frm.doc.att_fr_date,
- to_date: this.frm.doc.att_to_date,
- });
+ cmd: "erpnext.hr.doctype.upload_attendance.upload_attendance.get_template",
+ from_date: this.frm.doc.att_fr_date,
+ to_date: this.frm.doc.att_to_date,
+ });
},
show_upload() {
diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
index edf05e827b..674c8e3eb4 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
@@ -28,7 +28,12 @@ def get_template():
w = UnicodeWriter()
w = add_header(w)
- w = add_data(w, args)
+ try:
+ w = add_data(w, args)
+ except Exception as e:
+ frappe.clear_messages()
+ frappe.respond_as_web_page("Holiday List Missing", html=e)
+ return
# write out response as a type csv
frappe.response['result'] = cstr(w.getvalue())
diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json
index 3bdd1ce56e..fc59c19325 100644
--- a/erpnext/loan_management/desk_page/loan/loan.json
+++ b/erpnext/loan_management/desk_page/loan/loan.json
@@ -3,7 +3,7 @@
{
"hidden": 0,
"label": "Loan",
- "links": "[\n {\n \"description\": \"Loan Type for interest and penalty rates\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Applications from customers and employees.\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n { \"dependencies\": [\n \"Loan Type\"\n ],\n \"description\": \"Loans provided to customers and employees.\",\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"description\": \"Loan Type for interest and penalty rates\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Applications from customers and employees.\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loans provided to customers and employees.\",\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
@@ -13,7 +13,7 @@
{
"hidden": 0,
"label": "Disbursement and Repayment",
- "links": "[\n {\n \"label\": \"Loan Disbursement\",\n \"name\": \"Loan Disbursement\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Repayment\",\n \"name\": \"Loan Repayment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Interest Accrual\",\n \"name\": \"Loan Interest Accrual\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"label\": \"Loan Disbursement\",\n \"name\": \"Loan Disbursement\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Repayment\",\n \"name\": \"Loan Repayment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Write Off\",\n \"name\": \"Loan Write Off\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Interest Accrual\",\n \"name\": \"Loan Interest Accrual\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
@@ -34,10 +34,11 @@
"docstatus": 0,
"doctype": "Desk Page",
"extends_another_page": 0,
+ "hide_custom": 0,
"idx": 0,
"is_standard": 1,
"label": "Loan",
- "modified": "2020-06-07 19:42:14.947902",
+ "modified": "2020-10-17 12:59:50.336085",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan/loan.js b/erpnext/loan_management/doctype/loan/loan.js
index 9b4c21770e..28af3a9c41 100644
--- a/erpnext/loan_management/doctype/loan/loan.js
+++ b/erpnext/loan_management/doctype/loan/loan.js
@@ -7,10 +7,14 @@ frappe.ui.form.on('Loan', {
setup: function(frm) {
frm.make_methods = {
'Loan Disbursement': function() { frm.trigger('make_loan_disbursement') },
- 'Loan Security Unpledge': function() { frm.trigger('create_loan_security_unpledge') }
+ 'Loan Security Unpledge': function() { frm.trigger('create_loan_security_unpledge') },
+ 'Loan Write Off': function() { frm.trigger('make_loan_write_off_entry') }
}
},
onload: function (frm) {
+ // Ignore loan security pledge on cancel of loan
+ frm.ignore_doctypes_on_cancel_all = ["Loan Security Pledge"];
+
frm.set_query("loan_application", function () {
return {
"filters": {
@@ -21,6 +25,14 @@ frappe.ui.form.on('Loan', {
};
});
+ frm.set_query("loan_type", function () {
+ return {
+ "filters": {
+ "docstatus": 1
+ }
+ };
+ });
+
$.each(["penalty_income_account", "interest_income_account"], function(i, field) {
frm.set_query(field, function () {
return {
@@ -49,24 +61,33 @@ frappe.ui.form.on('Loan', {
refresh: function (frm) {
if (frm.doc.docstatus == 1) {
- if (frm.doc.status == "Sanctioned" || frm.doc.status == 'Partially Disbursed') {
+ if (["Disbursed", "Partially Disbursed"].includes(frm.doc.status) && (!frm.doc.repay_from_salary)) {
+ frm.add_custom_button(__('Request Loan Closure'), function() {
+ frm.trigger("request_loan_closure");
+ },__('Status'));
+
+ frm.add_custom_button(__('Loan Repayment'), function() {
+ frm.trigger("make_repayment_entry");
+ },__('Create'));
+ }
+
+ if (["Sanctioned", "Partially Disbursed"].includes(frm.doc.status)) {
frm.add_custom_button(__('Loan Disbursement'), function() {
frm.trigger("make_loan_disbursement");
},__('Create'));
}
- if (["Disbursed", "Partially Disbursed"].includes(frm.doc.status) && (!frm.doc.repay_from_salary)) {
- frm.add_custom_button(__('Loan Repayment'), function() {
- frm.trigger("make_repayment_entry");
- },__('Create'));
-
- }
-
if (frm.doc.status == "Loan Closure Requested") {
frm.add_custom_button(__('Loan Security Unpledge'), function() {
frm.trigger("create_loan_security_unpledge");
},__('Create'));
}
+
+ if (["Loan Closure Requested", "Disbursed", "Partially Disbursed"].includes(frm.doc.status)) {
+ frm.add_custom_button(__('Loan Write Off'), function() {
+ frm.trigger("make_loan_write_off_entry");
+ },__('Create'));
+ }
}
frm.trigger("toggle_fields");
},
@@ -117,6 +138,38 @@ frappe.ui.form.on('Loan', {
})
},
+ make_loan_write_off_entry: function(frm) {
+ frappe.call({
+ args: {
+ "loan": frm.doc.name,
+ "company": frm.doc.company,
+ "as_dict": 1
+ },
+ method: "erpnext.loan_management.doctype.loan.loan.make_loan_write_off",
+ callback: function (r) {
+ if (r.message)
+ var doc = frappe.model.sync(r.message)[0];
+ frappe.set_route("Form", doc.doctype, doc.name);
+ }
+ })
+ },
+
+ request_loan_closure: function(frm) {
+ frappe.confirm(__("Do you really want to close this loan"),
+ function() {
+ frappe.call({
+ args: {
+ 'loan': frm.doc.name
+ },
+ method: "erpnext.loan_management.doctype.loan.loan.request_loan_closure",
+ callback: function() {
+ frm.reload_doc();
+ }
+ });
+ }
+ );
+ },
+
create_loan_security_unpledge: function(frm) {
frappe.call({
method: "erpnext.loan_management.doctype.loan.loan.unpledge_security",
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index aa5e21b426..e8ecf015c3 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -43,6 +43,7 @@
"section_break_17",
"total_payment",
"total_principal_paid",
+ "written_off_amount",
"column_break_19",
"total_interest_payable",
"total_amount_paid",
@@ -75,6 +76,7 @@
"fieldname": "loan_application",
"fieldtype": "Link",
"label": "Loan Application",
+ "no_copy": 1,
"options": "Loan Application"
},
{
@@ -134,6 +136,7 @@
"fieldname": "loan_amount",
"fieldtype": "Currency",
"label": "Loan Amount",
+ "non_negative": 1,
"options": "Company:company:default_currency"
},
{
@@ -148,7 +151,8 @@
"depends_on": "eval:doc.status==\"Disbursed\"",
"fieldname": "disbursement_date",
"fieldtype": "Date",
- "label": "Disbursement Date"
+ "label": "Disbursement Date",
+ "no_copy": 1
},
{
"depends_on": "is_term_loan",
@@ -252,6 +256,7 @@
"fieldname": "total_payment",
"fieldtype": "Currency",
"label": "Total Payable Amount",
+ "no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
@@ -265,6 +270,7 @@
"fieldname": "total_interest_payable",
"fieldtype": "Currency",
"label": "Total Interest Payable",
+ "no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
@@ -273,6 +279,7 @@
"fieldname": "total_amount_paid",
"fieldtype": "Currency",
"label": "Total Amount Paid",
+ "no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
@@ -289,8 +296,7 @@
"default": "0",
"fieldname": "is_secured_loan",
"fieldtype": "Check",
- "label": "Is Secured Loan",
- "read_only": 1
+ "label": "Is Secured Loan"
},
{
"default": "0",
@@ -313,6 +319,7 @@
"fieldname": "total_principal_paid",
"fieldtype": "Currency",
"label": "Total Principal Paid",
+ "no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
@@ -320,6 +327,7 @@
"fieldname": "disbursed_amount",
"fieldtype": "Currency",
"label": "Disbursed Amount",
+ "no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
@@ -328,13 +336,23 @@
"fieldname": "maximum_loan_amount",
"fieldtype": "Currency",
"label": "Maximum Loan Amount",
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "written_off_amount",
+ "fieldtype": "Currency",
+ "label": "Written Off Amount",
+ "no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
}
],
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-08-01 12:36:11.255233",
+ "modified": "2020-11-05 10:04:00.762975",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index d1b7589a17..8405d6ec62 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -9,6 +9,7 @@ from frappe import _
from frappe.utils import flt, rounded, add_months, nowdate, getdate, now_datetime
from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
from erpnext.controllers.accounts_controller import AccountsController
+from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
class Loan(AccountsController):
def validate(self):
@@ -137,9 +138,12 @@ class Loan(AccountsController):
})
def unlink_loan_security_pledge(self):
- frappe.db.sql("""UPDATE `tabLoan Security Pledge` SET
- loan = '', status = 'Unpledged'
- where name = %s """, (self.loan_security_pledge))
+ pledges = frappe.get_all('Loan Security Pledge', fields=['name'], filters={'loan': self.name})
+ pledge_list = [d.name for d in pledges]
+ if pledge_list:
+ frappe.db.sql("""UPDATE `tabLoan Security Pledge` SET
+ loan = '', status = 'Unpledged'
+ where name in (%s) """ % (', '.join(['%s']*len(pledge_list))), tuple(pledge_list)) #nosec
def update_total_amount_paid(doc):
total_amount_paid = 0
@@ -182,6 +186,24 @@ def get_monthly_repayment_amount(repayment_method, loan_amount, rate_of_interest
monthly_repayment_amount = math.ceil(flt(loan_amount) / repayment_periods)
return monthly_repayment_amount
+@frappe.whitelist()
+def request_loan_closure(loan, posting_date=None):
+ if not posting_date:
+ posting_date = getdate()
+
+ amounts = calculate_amounts(loan, posting_date)
+ pending_amount = amounts['payable_amount'] + amounts['unaccrued_interest']
+
+ loan_type = frappe.get_value('Loan', loan, 'loan_type')
+ write_off_limit = frappe.get_value('Loan Type', loan_type, 'write_off_amount')
+
+ # checking greater than 0 as there may be some minor precision error
+ if pending_amount < write_off_limit:
+ # update status as loan closure requested
+ frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested')
+ else:
+ frappe.throw(_("Cannot close loan as there is an outstanding of {0}").format(pending_amount))
+
@frappe.whitelist()
def get_loan_application(loan_application):
loan = frappe.get_doc("Loan Application", loan_application)
@@ -200,6 +222,7 @@ def make_loan_disbursement(loan, company, applicant_type, applicant, pending_amo
disbursement_entry.applicant = applicant
disbursement_entry.company = company
disbursement_entry.disbursement_date = nowdate()
+ disbursement_entry.posting_date = nowdate()
disbursement_entry.disbursed_amount = pending_amount
if as_dict:
@@ -222,6 +245,38 @@ def make_repayment_entry(loan, applicant_type, applicant, loan_type, company, as
else:
return repayment_entry
+@frappe.whitelist()
+def make_loan_write_off(loan, company=None, posting_date=None, amount=0, as_dict=0):
+ if not company:
+ company = frappe.get_value('Loan', loan, 'company')
+
+ if not posting_date:
+ posting_date = getdate()
+
+ amounts = calculate_amounts(loan, posting_date)
+ pending_amount = amounts['pending_principal_amount']
+
+ if amount and (amount > pending_amount):
+ frappe.throw('Write Off amount cannot be greater than pending loan amount')
+
+ if not amount:
+ amount = pending_amount
+
+ # get default write off account from company master
+ write_off_account = frappe.get_value('Company', company, 'write_off_account')
+
+ write_off = frappe.new_doc('Loan Write Off')
+ write_off.loan = loan
+ write_off.posting_date = posting_date
+ write_off.write_off_account = write_off_account
+ write_off.write_off_amount = amount
+ write_off.save()
+
+ if as_dict:
+ return write_off.as_dict()
+ else:
+ return write_off
+
@frappe.whitelist()
def unpledge_security(loan=None, loan_security_pledge=None, as_dict=0, save=0, submit=0, approve=0):
# if loan is passed it will be considered as full unpledge
diff --git a/erpnext/loan_management/doctype/loan/loan_dashboard.py b/erpnext/loan_management/doctype/loan/loan_dashboard.py
index 90d5ae2650..7a8190f745 100644
--- a/erpnext/loan_management/doctype/loan/loan_dashboard.py
+++ b/erpnext/loan_management/doctype/loan/loan_dashboard.py
@@ -13,7 +13,7 @@ def get_data():
'items': ['Loan Security Pledge', 'Loan Security Shortfall', 'Loan Disbursement']
},
{
- 'items': ['Loan Repayment', 'Loan Interest Accrual', 'Loan Security Unpledge']
+ 'items': ['Loan Repayment', 'Loan Interest Accrual', 'Loan Write Off', 'Loan Security Unpledge']
}
]
}
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan/loan_list.js b/erpnext/loan_management/doctype/loan/loan_list.js
new file mode 100644
index 0000000000..6591b72996
--- /dev/null
+++ b/erpnext/loan_management/doctype/loan/loan_list.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.listview_settings['Loan'] = {
+ get_indicator: function(doc) {
+ var status_color = {
+ "Draft": "red",
+ "Sanctioned": "blue",
+ "Disbursed": "orange",
+ "Partially Disbursed": "yellow",
+ "Loan Closure Requested": "green",
+ "Closed": "green"
+ };
+ return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
+ },
+};
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 5a4a19a0fb..10a7b1143d 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -14,7 +14,7 @@ from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_
process_loan_interest_accrual_for_term_loans)
from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import create_process_loan_security_shortfall
-from erpnext.loan_management.doctype.loan.loan import unpledge_security
+from erpnext.loan_management.doctype.loan.loan import unpledge_security, request_loan_closure, make_loan_write_off
from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
from erpnext.loan_management.doctype.loan_disbursement.loan_disbursement import get_disbursal_amount
@@ -132,7 +132,7 @@ class TestLoan(unittest.TestCase):
loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
create_pledge(loan_application)
- loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit()
self.assertEquals(loan.loan_amount, 1000000)
@@ -142,30 +142,30 @@ class TestLoan(unittest.TestCase):
no_of_days = date_diff(last_date, first_date) + 1
- accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
- / (days_in_year(get_datetime(first_date).year) * 100)
+ accrued_interest_amount = flt((loan.loan_amount * loan.rate_of_interest * no_of_days)
+ / (days_in_year(get_datetime(first_date).year) * 100), 2)
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
- repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 10), "Regular Payment", 111118.68)
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 10), 111119)
repayment_entry.save()
repayment_entry.submit()
- penalty_amount = (accrued_interest_amount * 4 * 25) / (100 * days_in_year(get_datetime(first_date).year))
- self.assertEquals(flt(repayment_entry.penalty_amount, 2), flt(penalty_amount, 2))
+ penalty_amount = (accrued_interest_amount * 5 * 25) / 100
+ self.assertEquals(flt(repayment_entry.penalty_amount,0), flt(penalty_amount, 0))
- amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
- 'paid_principal_amount'])
+ amounts = frappe.db.get_all('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount'])
loan.load_from_db()
- self.assertEquals(amounts[0], repayment_entry.interest_payable)
- self.assertEquals(flt(loan.total_principal_paid, 2), flt(repayment_entry.amount_paid -
- penalty_amount - amounts[0], 2))
+ total_interest_paid = amounts[0]['paid_interest_amount'] + amounts[1]['paid_interest_amount']
+ self.assertEquals(amounts[1]['paid_interest_amount'], repayment_entry.interest_payable)
+ self.assertEquals(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid -
+ penalty_amount - total_interest_paid, 0))
- def test_loan_closure_repayment(self):
+ def test_loan_closure(self):
pledge = [{
"loan_security": "Test Security 1",
"qty": 4000.00
@@ -174,7 +174,7 @@ class TestLoan(unittest.TestCase):
loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
create_pledge(loan_application)
- loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit()
self.assertEquals(loan.loan_amount, 1000000)
@@ -184,10 +184,10 @@ class TestLoan(unittest.TestCase):
no_of_days = date_diff(last_date, first_date) + 1
- # Adding 6 since repayment is made 5 days late after due date
+ # Adding 5 since repayment is made 5 days late after due date
# and since payment type is loan closure so interest should be considered for those
- # 6 days as well though in grace period
- no_of_days += 6
+ # 5 days as well though in grace period
+ no_of_days += 5
accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
/ (days_in_year(get_datetime(first_date).year) * 100)
@@ -195,15 +195,17 @@ class TestLoan(unittest.TestCase):
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
- repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 6),
- "Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
+ flt(loan.loan_amount + accrued_interest_amount))
+
repayment_entry.submit()
amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
- self.assertEquals(flt(amount, 2),flt(accrued_interest_amount, 2))
+ self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0))
self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0)
+ request_loan_closure(loan.name)
loan.load_from_db()
self.assertEquals(loan.status, "Loan Closure Requested")
@@ -230,8 +232,7 @@ class TestLoan(unittest.TestCase):
process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
- repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(nowdate(), 5),
- "Regular Payment", 89768.75)
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(nowdate(), 5), 89768.75)
repayment_entry.submit()
@@ -281,7 +282,7 @@ class TestLoan(unittest.TestCase):
loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
create_pledge(loan_application)
- loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit()
self.assertEquals(loan.loan_amount, 1000000)
@@ -291,7 +292,7 @@ class TestLoan(unittest.TestCase):
no_of_days = date_diff(last_date, first_date) + 1
- no_of_days += 6
+ no_of_days += 5
accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
/ (days_in_year(get_datetime(first_date).year) * 100)
@@ -299,10 +300,10 @@ class TestLoan(unittest.TestCase):
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
- repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 6),
- "Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), flt(loan.loan_amount + accrued_interest_amount))
repayment_entry.submit()
+ request_loan_closure(loan.name)
loan.load_from_db()
self.assertEquals(loan.status, "Loan Closure Requested")
@@ -317,9 +318,9 @@ class TestLoan(unittest.TestCase):
self.assertEqual(loan.status, 'Closed')
self.assertEquals(sum(pledged_qty.values()), 0)
- amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment")
- self.assertEqual(amounts['pending_principal_amount'], 0)
- self.assertEqual(amounts['payable_principal_amount'], 0)
+ amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 5))
+ self.assertTrue(amounts['pending_principal_amount'] < 0)
+ self.assertEquals(amounts['payable_principal_amount'], 0.0)
self.assertEqual(amounts['interest_amount'], 0)
def test_disbursal_check_with_shortfall(self):
@@ -381,7 +382,7 @@ class TestLoan(unittest.TestCase):
loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
create_pledge(loan_application)
- loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit()
self.assertEquals(loan.loan_amount, 1000000)
@@ -391,7 +392,7 @@ class TestLoan(unittest.TestCase):
no_of_days = date_diff(last_date, first_date) + 1
- no_of_days += 6
+ no_of_days += 5
accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
/ (days_in_year(get_datetime(first_date).year) * 100)
@@ -399,20 +400,192 @@ class TestLoan(unittest.TestCase):
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
- amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment")
+ amounts = calculate_amounts(loan.name, add_days(last_date, 5))
- repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 6),
- "Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), flt(loan.loan_amount + accrued_interest_amount))
repayment_entry.submit()
amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
'paid_principal_amount'])
+ request_loan_closure(loan.name)
loan.load_from_db()
self.assertEquals(loan.status, "Loan Closure Requested")
- amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment")
- self.assertEquals(amounts['pending_principal_amount'], 0.0)
+ amounts = calculate_amounts(loan.name, add_days(last_date, 5))
+ self.assertTrue(amounts['pending_principal_amount'] < 0.0)
+
+ def test_partial_unaccrued_interest_payment(self):
+ pledge = [{
+ "loan_security": "Test Security 1",
+ "qty": 4000.00
+ }]
+
+ loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+ create_pledge(loan_application)
+
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+ loan.submit()
+
+ self.assertEquals(loan.loan_amount, 1000000)
+
+ first_date = '2019-10-01'
+ last_date = '2019-10-30'
+
+ no_of_days = date_diff(last_date, first_date) + 1
+
+ no_of_days += 5.5
+
+ # get partial unaccrued interest amount
+ paid_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
+ / (days_in_year(get_datetime(first_date).year) * 100)
+
+ make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+ process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+
+ amounts = calculate_amounts(loan.name, add_days(last_date, 5))
+
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
+ paid_amount)
+
+ repayment_entry.submit()
+ repayment_entry.load_from_db()
+
+ partial_accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * 5) \
+ / (days_in_year(get_datetime(first_date).year) * 100)
+
+ interest_amount = flt(amounts['interest_amount'] + partial_accrued_interest_amount, 2)
+ self.assertEqual(flt(repayment_entry.total_interest_paid, 0), flt(interest_amount, 0))
+
+ def test_penalty(self):
+ pledge = [{
+ "loan_security": "Test Security 1",
+ "qty": 4000.00
+ }]
+
+ loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+ create_pledge(loan_application)
+
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+ loan.submit()
+
+ self.assertEquals(loan.loan_amount, 1000000)
+
+ first_date = '2019-10-01'
+ last_date = '2019-10-30'
+
+ make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+ process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+
+ amounts = calculate_amounts(loan.name, add_days(last_date, 1))
+ paid_amount = amounts['interest_amount']/2
+
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
+ paid_amount)
+
+ repayment_entry.submit()
+
+ # 30 days - grace period
+ penalty_days = 30 - 4
+ penalty_applicable_amount = flt(amounts['interest_amount']/2, 2)
+ penalty_amount = flt((((penalty_applicable_amount * 25) / 100) * penalty_days), 2)
+ process = process_loan_interest_accrual_for_demand_loans(posting_date = '2019-11-30')
+
+ calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual',
+ {'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount')
+
+ self.assertEquals(calculated_penalty_amount, penalty_amount)
+
+ def test_loan_write_off_limit(self):
+ pledge = [{
+ "loan_security": "Test Security 1",
+ "qty": 4000.00
+ }]
+
+ loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+ create_pledge(loan_application)
+
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+ loan.submit()
+
+ self.assertEquals(loan.loan_amount, 1000000)
+
+ first_date = '2019-10-01'
+ last_date = '2019-10-30'
+
+ no_of_days = date_diff(last_date, first_date) + 1
+ no_of_days += 5
+
+ accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
+ / (days_in_year(get_datetime(first_date).year) * 100)
+
+ make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+ process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+
+ # repay 50 less so that it can be automatically written off
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
+ flt(loan.loan_amount + accrued_interest_amount - 50))
+
+ repayment_entry.submit()
+
+ amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
+
+ self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0))
+ self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0)
+
+ amounts = calculate_amounts(loan.name, add_days(last_date, 5))
+ self.assertEquals(flt(amounts['pending_principal_amount'], 0), 50)
+
+ request_loan_closure(loan.name)
+ loan.load_from_db()
+ self.assertEquals(loan.status, "Loan Closure Requested")
+
+ def test_loan_amount_write_off(self):
+ pledge = [{
+ "loan_security": "Test Security 1",
+ "qty": 4000.00
+ }]
+
+ loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+ create_pledge(loan_application)
+
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+ loan.submit()
+
+ self.assertEquals(loan.loan_amount, 1000000)
+
+ first_date = '2019-10-01'
+ last_date = '2019-10-30'
+
+ no_of_days = date_diff(last_date, first_date) + 1
+ no_of_days += 5
+
+ accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
+ / (days_in_year(get_datetime(first_date).year) * 100)
+
+ make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+ process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+
+ # repay 100 less so that it can be automatically written off
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
+ flt(loan.loan_amount + accrued_interest_amount - 100))
+
+ repayment_entry.submit()
+
+ amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
+
+ self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0))
+ self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0)
+
+ amounts = calculate_amounts(loan.name, add_days(last_date, 5))
+ self.assertEquals(flt(amounts['pending_principal_amount'], 0), 100)
+
+ we = make_loan_write_off(loan.name, amount=amounts['pending_principal_amount'])
+ we.submit()
+
+ amounts = calculate_amounts(loan.name, add_days(last_date, 5))
+ self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0)
+
def create_loan_accounts():
if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
@@ -496,7 +669,8 @@ def create_loan_type(loan_name, maximum_loan_amount, rate_of_interest, penalty_i
"interest_income_account": interest_income_account,
"penalty_income_account": penalty_income_account,
"repayment_method": repayment_method,
- "repayment_periods": repayment_periods
+ "repayment_periods": repayment_periods,
+ "write_off_amount": 100
}).insert()
loan_type.submit()
@@ -532,7 +706,7 @@ def create_loan_security():
"haircut": 50.00,
}).insert(ignore_permissions=True)
-def create_loan_security_pledge(applicant, pledges, loan_application):
+def create_loan_security_pledge(applicant, pledges, loan_application=None, loan=None):
lsp = frappe.new_doc("Loan Security Pledge")
lsp.applicant_type = 'Customer'
@@ -540,11 +714,13 @@ def create_loan_security_pledge(applicant, pledges, loan_application):
lsp.company = "_Test Company"
lsp.loan_application = loan_application
+ if loan:
+ lsp.loan = loan
+
for pledge in pledges:
lsp.append('securities', {
"loan_security": pledge['loan_security'],
- "qty": pledge['qty'],
- "haircut": pledge['haircut']
+ "qty": pledge['qty']
})
lsp.save()
@@ -582,12 +758,11 @@ def create_loan_security_price(loan_security, loan_security_price, uom, from_dat
"valid_upto": to_date
}).insert(ignore_permissions=True)
-def create_repayment_entry(loan, applicant, posting_date, payment_type, paid_amount):
+def create_repayment_entry(loan, applicant, posting_date, paid_amount):
lr = frappe.get_doc({
"doctype": "Loan Repayment",
"against_loan": loan,
- "payment_type": payment_type,
"company": "_Test Company",
"posting_date": posting_date or nowdate(),
"applicant": applicant,
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index bac6e638d7..e59db4c12d 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -127,6 +127,7 @@ def create_loan(source_name, target_doc=None, submit=0):
target_doc.loan_account = account_details.loan_account
target_doc.interest_income_account = account_details.interest_income_account
target_doc.penalty_income_account = account_details.penalty_income_account
+ target_doc.loan_application = source_name
doclist = get_mapped_doc("Loan Application", source_name, {
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json
index c437a987eb..cd5df4d3cd 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json
@@ -26,19 +26,24 @@
{
"fieldname": "against_loan",
"fieldtype": "Link",
+ "in_list_view": 1,
"label": "Against Loan ",
- "options": "Loan"
+ "options": "Loan",
+ "reqd": 1
},
{
"fieldname": "disbursement_date",
"fieldtype": "Date",
- "label": "Disbursement Date"
+ "label": "Disbursement Date",
+ "reqd": 1
},
{
"fieldname": "disbursed_amount",
"fieldtype": "Currency",
"label": "Disbursed Amount",
- "options": "Company:company:default_currency"
+ "non_negative": 1,
+ "options": "Company:company:default_currency",
+ "reqd": 1
},
{
"fieldname": "amended_from",
@@ -53,17 +58,21 @@
"fetch_from": "against_loan.company",
"fieldname": "company",
"fieldtype": "Link",
+ "in_list_view": 1,
"label": "Company",
"options": "Company",
- "read_only": 1
+ "read_only": 1,
+ "reqd": 1
},
{
"fetch_from": "against_loan.applicant",
"fieldname": "applicant",
"fieldtype": "Dynamic Link",
+ "in_list_view": 1,
"label": "Applicant",
"options": "applicant_type",
- "read_only": 1
+ "read_only": 1,
+ "reqd": 1
},
{
"collapsible": 1,
@@ -102,9 +111,11 @@
"fetch_from": "against_loan.applicant_type",
"fieldname": "applicant_type",
"fieldtype": "Select",
+ "in_list_view": 1,
"label": "Applicant Type",
"options": "Employee\nMember\nCustomer",
- "read_only": 1
+ "read_only": 1,
+ "reqd": 1
},
{
"fieldname": "bank_account",
@@ -117,9 +128,10 @@
"fieldtype": "Column Break"
}
],
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-04-29 05:20:41.629911",
+ "modified": "2020-11-06 10:04:30.882322",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Disbursement",
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index 260fada893..233862bcfe 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -17,6 +17,7 @@ class LoanDisbursement(AccountsController):
def validate(self):
self.set_missing_values()
+ self.validate_disbursal_amount()
def on_submit(self):
self.set_status_and_amounts()
@@ -40,57 +41,21 @@ class LoanDisbursement(AccountsController):
if not self.bank_account and self.applicant_type == "Customer":
self.bank_account = frappe.db.get_value("Customer", self.applicant, "default_bank_account")
- def set_status_and_amounts(self, cancel=0):
+ def validate_disbursal_amount(self):
+ possible_disbursal_amount = get_disbursal_amount(self.against_loan)
+ if self.disbursed_amount > possible_disbursal_amount:
+ frappe.throw(_("Disbursed Amount cannot be greater than {0}").format(possible_disbursal_amount))
+
+ def set_status_and_amounts(self, cancel=0):
loan_details = frappe.get_all("Loan",
fields = ["loan_amount", "disbursed_amount", "total_payment", "total_principal_paid", "total_interest_payable",
"status", "is_term_loan", "is_secured_loan"], filters= { "name": self.against_loan })[0]
if cancel:
- disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount
- total_payment = loan_details.total_payment
-
- if loan_details.disbursed_amount > loan_details.loan_amount:
- topup_amount = loan_details.disbursed_amount - loan_details.loan_amount
- if topup_amount > self.disbursed_amount:
- topup_amount = self.disbursed_amount
-
- total_payment = total_payment - topup_amount
-
- if disbursed_amount == 0:
- status = "Sanctioned"
- elif disbursed_amount >= loan_details.loan_amount:
- status = "Disbursed"
- else:
- status = "Partially Disbursed"
+ disbursed_amount, status, total_payment = self.get_values_on_cancel(loan_details)
else:
- disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount
- total_payment = loan_details.total_payment
-
- possible_disbursal_amount = get_disbursal_amount(self.against_loan)
-
- if self.disbursed_amount > possible_disbursal_amount:
- frappe.throw(_("Disbursed Amount cannot be greater than {0}").format(possible_disbursal_amount))
-
- if loan_details.status == "Disbursed" and not loan_details.is_term_loan:
- process_loan_interest_accrual_for_demand_loans(posting_date=add_days(self.disbursement_date, -1),
- loan=self.against_loan)
-
- if disbursed_amount > loan_details.loan_amount:
- topup_amount = disbursed_amount - loan_details.loan_amount
-
- if topup_amount < 0:
- topup_amount = 0
-
- if topup_amount > self.disbursed_amount:
- topup_amount = self.disbursed_amount
-
- total_payment = total_payment + topup_amount
-
- if flt(disbursed_amount) >= loan_details.loan_amount:
- status = "Disbursed"
- else:
- status = "Partially Disbursed"
+ disbursed_amount, status, total_payment = self.get_values_on_submit(loan_details)
frappe.db.set_value("Loan", self.against_loan, {
"disbursement_date": self.disbursement_date,
@@ -99,6 +64,53 @@ class LoanDisbursement(AccountsController):
"total_payment": total_payment
})
+ def get_values_on_cancel(self, loan_details):
+ disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount
+ total_payment = loan_details.total_payment
+
+ if loan_details.disbursed_amount > loan_details.loan_amount:
+ topup_amount = loan_details.disbursed_amount - loan_details.loan_amount
+ if topup_amount > self.disbursed_amount:
+ topup_amount = self.disbursed_amount
+
+ total_payment = total_payment - topup_amount
+
+ if disbursed_amount == 0:
+ status = "Sanctioned"
+
+ elif disbursed_amount >= loan_details.loan_amount:
+ status = "Disbursed"
+ else:
+ status = "Partially Disbursed"
+
+ return disbursed_amount, status, total_payment
+
+ def get_values_on_submit(self, loan_details):
+ disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount
+ total_payment = loan_details.total_payment
+
+ if loan_details.status in ("Disbursed", "Partially Disbursed") and not loan_details.is_term_loan:
+ process_loan_interest_accrual_for_demand_loans(posting_date=add_days(self.disbursement_date, -1),
+ loan=self.against_loan, accrual_type="Disbursement")
+
+ if disbursed_amount > loan_details.loan_amount:
+ topup_amount = disbursed_amount - loan_details.loan_amount
+
+ if topup_amount < 0:
+ topup_amount = 0
+
+ if topup_amount > self.disbursed_amount:
+ topup_amount = self.disbursed_amount
+
+ total_payment = total_payment + topup_amount
+
+ if flt(disbursed_amount) >= loan_details.loan_amount:
+ status = "Disbursed"
+ else:
+ status = "Partially Disbursed"
+
+ return disbursed_amount, status, total_payment
+
def make_gl_entries(self, cancel=0, adv_adj=0):
gle_map = []
loan_details = frappe.get_doc("Loan", self.against_loan)
@@ -111,7 +123,7 @@ class LoanDisbursement(AccountsController):
"debit_in_account_currency": self.disbursed_amount,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
- "remarks": "Against Loan:" + self.against_loan,
+ "remarks": _("Disbursement against loan:") + self.against_loan,
"cost_center": self.cost_center,
"party_type": self.applicant_type,
"party": self.applicant,
@@ -127,10 +139,8 @@ class LoanDisbursement(AccountsController):
"credit_in_account_currency": self.disbursed_amount,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
- "remarks": "Against Loan:" + self.against_loan,
+ "remarks": _("Disbursement against loan:") + self.against_loan,
"cost_center": self.cost_center,
- "party_type": self.applicant_type,
- "party": self.applicant,
"posting_date": self.disbursement_date
})
)
@@ -155,7 +165,8 @@ def get_total_pledged_security_value(loan):
pledged_securities = get_pledged_security_qty(loan)
for security, qty in pledged_securities.items():
- security_value += (loan_security_price_map.get(security) * qty * hair_cut_map.get(security))/100
+ after_haircut_percentage = 100 - hair_cut_map.get(security)
+ security_value += (loan_security_price_map.get(security) * qty * after_haircut_percentage)/100
return security_value
@@ -173,7 +184,8 @@ def get_disbursal_amount(loan):
pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
- flt(loan_details.total_principal_paid)
else:
- pending_principal_amount = flt(loan_details.disbursed_amount)
+ pending_principal_amount = flt(loan_details.disbursed_amount) - flt(loan_details.total_interest_payable) \
+ - flt(loan_details.total_principal_paid)
security_value = 0.0
if loan_details.is_secured_loan:
@@ -184,6 +196,9 @@ def get_disbursal_amount(loan):
disbursal_amount = flt(security_value) - flt(pending_principal_amount)
+ if loan_details.is_term_loan and (disbursal_amount + loan_details.loan_amount) > loan_details.loan_amount:
+ disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount
+
return disbursal_amount
diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
index 2cb2637612..a8753877a6 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
@@ -8,9 +8,10 @@ from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_la
from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_repayment_entry, create_loan_application,
make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_security_price)
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
-from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
+from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year, get_per_day_interest
from erpnext.selling.doctype.customer.test_customer import get_customer_dict
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
+from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
class TestLoanDisbursement(unittest.TestCase):
@@ -60,8 +61,7 @@ class TestLoanDisbursement(unittest.TestCase):
self.assertRaises(frappe.ValidationError, make_loan_disbursement_entry, loan.name,
500000, first_date)
- repayment_entry = create_repayment_entry(loan.name, self.applicant, add_days(get_last_day(nowdate()), 5),
- "Regular Payment", 611095.89)
+ repayment_entry = create_repayment_entry(loan.name, self.applicant, add_days(get_last_day(nowdate()), 5), 611095.89)
repayment_entry.submit()
loan.reload()
@@ -69,3 +69,50 @@ class TestLoanDisbursement(unittest.TestCase):
# After repayment loan disbursement entry should go through
make_loan_disbursement_entry(loan.name, 500000, disbursement_date=add_days(last_date, 16))
+ # check for disbursement accrual
+ loan_interest_accrual = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name,
+ 'accrual_type': 'Disbursement'})
+
+ self.assertTrue(loan_interest_accrual)
+
+ def test_loan_topup_with_additional_pledge(self):
+ pledge = [{
+ "loan_security": "Test Security 1",
+ "qty": 4000.00
+ }]
+
+ loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge)
+ create_pledge(loan_application)
+
+ loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date='2019-10-01')
+ loan.submit()
+
+ self.assertEquals(loan.loan_amount, 1000000)
+
+ first_date = '2019-10-01'
+ last_date = '2019-10-30'
+
+ # Disbursed 10,00,000 amount
+ make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+ process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+ amounts = calculate_amounts(loan.name, add_days(last_date, 1))
+
+ previous_interest = amounts['interest_amount']
+
+ pledge1 = [{
+ "loan_security": "Test Security 1",
+ "qty": 2000.00
+ }]
+
+ create_loan_security_pledge(self.applicant, pledge1, loan=loan.name)
+
+ # Topup 500000
+ make_loan_disbursement_entry(loan.name, 500000, disbursement_date=add_days(last_date, 1))
+ process_loan_interest_accrual_for_demand_loans(posting_date = add_days(last_date, 15))
+ amounts = calculate_amounts(loan.name, add_days(last_date, 15))
+
+ per_day_interest = get_per_day_interest(1500000, 13.5, '2019-10-30')
+ interest = per_day_interest * 15
+
+ self.assertEquals(amounts['pending_principal_amount'], 1500000)
+ self.assertEquals(amounts['interest_amount'], flt(interest + previous_interest, 2))
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json
index 5fc3e8f4b6..f157f0df8f 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json
@@ -14,6 +14,7 @@
"column_break_4",
"company",
"posting_date",
+ "accrual_type",
"is_term_loan",
"section_break_7",
"pending_principal_amount",
@@ -22,9 +23,11 @@
"column_break_14",
"interest_amount",
"paid_interest_amount",
+ "penalty_amount",
"section_break_15",
"process_loan_interest_accrual",
"repayment_schedule_name",
+ "last_accrual_date",
"amended_from"
],
"fields": [
@@ -139,6 +142,7 @@
"read_only": 1
},
{
+ "depends_on": "eval:doc.is_term_loan",
"fieldname": "paid_principal_amount",
"fieldtype": "Currency",
"label": "Paid Principal Amount",
@@ -149,12 +153,32 @@
"fieldtype": "Currency",
"label": "Paid Interest Amount",
"options": "Company:company:default_currency"
+ },
+ {
+ "fieldname": "accrual_type",
+ "fieldtype": "Select",
+ "label": "Accrual Type",
+ "options": "Regular\nRepayment\nDisbursement"
+ },
+ {
+ "fieldname": "penalty_amount",
+ "fieldtype": "Currency",
+ "label": "Penalty Amount",
+ "options": "Company:company:default_currency"
+ },
+ {
+ "fieldname": "last_accrual_date",
+ "fieldtype": "Date",
+ "hidden": 1,
+ "label": "Last Accrual Date",
+ "read_only": 1
}
],
"in_create": 1,
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-04-16 11:24:23.258404",
+ "modified": "2020-11-07 05:49:25.448875",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Interest Accrual",
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
index 2d959bf3be..d17f5af490 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
@@ -22,6 +22,8 @@ class LoanInterestAccrual(AccountsController):
if not self.interest_amount and not self.payable_principal_amount:
frappe.throw(_("Interest Amount or Principal Amount is mandatory"))
+ if not self.last_accrual_date:
+ self.last_accrual_date = get_last_accrual_date(self.loan)
def on_submit(self):
self.make_gl_entries()
@@ -50,7 +52,8 @@ class LoanInterestAccrual(AccountsController):
"debit_in_account_currency": self.interest_amount,
"against_voucher_type": "Loan",
"against_voucher": self.loan,
- "remarks": _("Against Loan:") + self.loan,
+ "remarks": _("Interest accrued from {0} to {1} against loan: {2}").format(
+ self.last_accrual_date, self.posting_date, self.loan),
"cost_center": erpnext.get_default_cost_center(self.company),
"posting_date": self.posting_date
})
@@ -59,14 +62,13 @@ class LoanInterestAccrual(AccountsController):
gle_map.append(
self.get_gl_dict({
"account": self.interest_income_account,
- "party_type": self.applicant_type,
- "party": self.applicant,
"against": self.loan_account,
"credit": self.interest_amount,
"credit_in_account_currency": self.interest_amount,
"against_voucher_type": "Loan",
"against_voucher": self.loan,
- "remarks": _("Against Loan:") + self.loan,
+ "remarks": ("Interest accrued from {0} to {1} against loan: {2}").format(
+ self.last_accrual_date, self.posting_date, self.loan),
"cost_center": erpnext.get_default_cost_center(self.company),
"posting_date": self.posting_date
})
@@ -79,19 +81,23 @@ class LoanInterestAccrual(AccountsController):
# For Eg: If Loan disbursement date is '01-09-2019' and disbursed amount is 1000000 and
# rate of interest is 13.5 then first loan interest accural will be on '01-10-2019'
# which means interest will be accrued for 30 days which should be equal to 11095.89
-def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest):
+def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest, accrual_type):
+ from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
+
no_of_days = get_no_of_days_for_interest_accural(loan, posting_date)
+ precision = cint(frappe.db.get_default("currency_precision")) or 2
if no_of_days <= 0:
return
if loan.status == 'Disbursed':
pending_principal_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \
- - flt(loan.total_principal_paid)
+ - flt(loan.total_principal_paid) - flt(loan.written_off_amount)
else:
- pending_principal_amount = loan.disbursed_amount
+ pending_principal_amount = flt(loan.disbursed_amount) - flt(loan.total_interest_payable) \
+ - flt(loan.total_principal_paid) - flt(loan.written_off_amount)
- interest_per_day = (pending_principal_amount * loan.rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)
+ interest_per_day = get_per_day_interest(pending_principal_amount, loan.rate_of_interest, posting_date)
payable_interest = interest_per_day * no_of_days
args = frappe._dict({
@@ -102,13 +108,16 @@ def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_i
'loan_account': loan.loan_account,
'pending_principal_amount': pending_principal_amount,
'interest_amount': payable_interest,
+ 'penalty_amount': calculate_amounts(loan.name, posting_date)['penalty_amount'],
'process_loan_interest': process_loan_interest,
- 'posting_date': posting_date
+ 'posting_date': posting_date,
+ 'accrual_type': accrual_type
})
- make_loan_interest_accrual_entry(args)
+ if flt(payable_interest, precision) > 0.0:
+ make_loan_interest_accrual_entry(args)
-def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_interest, open_loans=None, loan_type=None):
+def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_interest, open_loans=None, loan_type=None, accrual_type="Regular"):
query_filters = {
"status": ('in', ['Disbursed', 'Partially Disbursed']),
"docstatus": 1
@@ -123,13 +132,13 @@ def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_inte
open_loans = frappe.get_all("Loan",
fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account",
"is_term_loan", "status", "disbursement_date", "disbursed_amount", "applicant_type", "applicant",
- "rate_of_interest", "total_interest_payable", "total_principal_paid", "repayment_start_date"],
+ "rate_of_interest", "total_interest_payable", "written_off_amount", "total_principal_paid", "repayment_start_date"],
filters=query_filters)
for loan in open_loans:
- calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest)
+ calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest, accrual_type)
-def make_accrual_interest_entry_for_term_loans(posting_date, process_loan_interest, term_loan=None, loan_type=None):
+def make_accrual_interest_entry_for_term_loans(posting_date, process_loan_interest, term_loan=None, loan_type=None, accrual_type="Regular"):
curr_date = posting_date or add_days(nowdate(), 1)
term_loans = get_term_loans(curr_date, term_loan, loan_type)
@@ -148,7 +157,8 @@ def make_accrual_interest_entry_for_term_loans(posting_date, process_loan_intere
'payable_principal': loan.principal_amount,
'process_loan_interest': process_loan_interest,
'repayment_schedule_name': loan.payment_entry,
- 'posting_date': posting_date
+ 'posting_date': posting_date,
+ 'accrual_type': accrual_type
})
make_loan_interest_accrual_entry(args)
@@ -192,31 +202,33 @@ def make_loan_interest_accrual_entry(args):
loan_interest_accrual.loan_account = args.loan_account
loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, precision)
loan_interest_accrual.interest_amount = flt(args.interest_amount, precision)
+ loan_interest_accrual.penalty_amount = flt(args.penalty_amount, precision)
loan_interest_accrual.posting_date = args.posting_date or nowdate()
loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest
loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name
loan_interest_accrual.payable_principal_amount = args.payable_principal
+ loan_interest_accrual.accrual_type = args.accrual_type
loan_interest_accrual.save()
loan_interest_accrual.submit()
def get_no_of_days_for_interest_accural(loan, posting_date):
- last_interest_accrual_date = get_last_accural_date_in_current_month(loan)
+ last_interest_accrual_date = get_last_accrual_date(loan.name)
no_of_days = date_diff(posting_date or nowdate(), last_interest_accrual_date) + 1
return no_of_days
-def get_last_accural_date_in_current_month(loan):
+def get_last_accrual_date(loan):
last_posting_date = frappe.db.sql(""" SELECT MAX(posting_date) from `tabLoan Interest Accrual`
- WHERE loan = %s""", (loan.name))
+ WHERE loan = %s and docstatus = 1""", (loan))
if last_posting_date[0][0]:
# interest for last interest accrual date is already booked, so add 1 day
return add_days(last_posting_date[0][0], 1)
else:
- return loan.disbursement_date
+ return frappe.db.get_value('Loan', loan, 'disbursement_date')
def days_in_year(year):
days = 365
@@ -226,3 +238,11 @@ def days_in_year(year):
return days
+def get_per_day_interest(principal_amount, rate_of_interest, posting_date=None):
+ if not posting_date:
+ posting_date = getdate()
+
+ precision = cint(frappe.db.get_default("currency_precision")) or 2
+
+ return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100), precision)
+
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
index 4b85b21869..46a6440553 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date)
-from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_loan_security_price,
+from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_price,
make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_application)
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
@@ -57,4 +57,4 @@ class TestLoanInterestAccrual(unittest.TestCase):
loan_interest_accural = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name})
- self.assertEquals(flt(loan_interest_accural.interest_amount, 2), flt(accrued_interest_amount, 2))
+ self.assertEquals(flt(loan_interest_accural.interest_amount, 0), flt(accrued_interest_amount, 0))
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
index 5942455919..2b5df4be24 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
@@ -10,11 +10,11 @@
"applicant_type",
"applicant",
"loan_type",
- "payment_type",
"column_break_3",
"company",
"posting_date",
"is_term_loan",
+ "rate_of_interest",
"payment_details_section",
"due_date",
"pending_principal_amount",
@@ -31,6 +31,7 @@
"column_break_21",
"reference_date",
"principal_amount_paid",
+ "total_interest_paid",
"repayment_details",
"amended_from"
],
@@ -95,15 +96,6 @@
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
- {
- "default": "Regular Payment",
- "fieldname": "payment_type",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Payment Type",
- "options": "\nRegular Payment\nLoan Closure",
- "reqd": 1
- },
{
"fieldname": "payable_amount",
"fieldtype": "Currency",
@@ -116,6 +108,7 @@
"fieldname": "amount_paid",
"fieldtype": "Currency",
"label": "Amount Paid",
+ "non_negative": 1,
"options": "Company:company:default_currency",
"reqd": 1
},
@@ -195,6 +188,7 @@
"fieldtype": "Currency",
"hidden": 1,
"label": "Principal Amount Paid",
+ "options": "Company:company:default_currency",
"read_only": 1
},
{
@@ -217,11 +211,27 @@
"hidden": 1,
"label": "Repayment Details",
"options": "Loan Repayment Detail"
+ },
+ {
+ "fieldname": "total_interest_paid",
+ "fieldtype": "Currency",
+ "hidden": 1,
+ "label": "Total Interest Paid",
+ "options": "Company:company:default_currency",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "loan_type.rate_of_interest",
+ "fieldname": "rate_of_interest",
+ "fieldtype": "Percent",
+ "label": "Rate Of Interest",
+ "read_only": 1
}
],
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-05-16 09:40:15.581165",
+ "modified": "2020-11-05 10:06:58.792841",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Repayment",
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 97dbc44bf1..415ba993c7 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -14,14 +14,15 @@ from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import update_shortfall_status
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
+from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import get_per_day_interest, get_last_accrual_date
class LoanRepayment(AccountsController):
def validate(self):
- amounts = calculate_amounts(self.against_loan, self.posting_date, self.payment_type)
+ amounts = calculate_amounts(self.against_loan, self.posting_date)
self.set_missing_values(amounts)
self.validate_amount()
- self.allocate_amounts(amounts['pending_accrual_entries'])
+ self.allocate_amounts(amounts)
def before_submit(self):
self.book_unaccrued_interest()
@@ -32,8 +33,8 @@ class LoanRepayment(AccountsController):
def on_cancel(self):
self.mark_as_unpaid()
- self.make_gl_entries(cancel=1)
self.ignore_linked_doctypes = ['GL Entry']
+ self.make_gl_entries(cancel=1)
def set_missing_values(self, amounts):
precision = cint(frappe.db.get_default("currency_precision")) or 2
@@ -72,29 +73,36 @@ class LoanRepayment(AccountsController):
msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount)
frappe.throw(msg)
- if self.payment_type == "Loan Closure" and flt(self.amount_paid, precision) < flt(self.payable_amount, precision):
- msg = _("Amount of {0} is required for Loan closure").format(self.payable_amount)
- frappe.throw(msg)
-
def book_unaccrued_interest(self):
- if self.payment_type == 'Loan Closure':
- total_interest_paid = 0
- for payment in self.repayment_details:
- total_interest_paid += payment.paid_interest_amount
+ precision = cint(frappe.db.get_default("currency_precision")) or 2
+ if self.total_interest_paid > self.interest_payable:
+ if not self.is_term_loan:
+ # get last loan interest accrual date
+ last_accrual_date = get_last_accrual_date(self.against_loan)
- if total_interest_paid < self.interest_payable:
- if not self.is_term_loan:
- process = process_loan_interest_accrual_for_demand_loans(posting_date=self.posting_date,
- loan=self.against_loan)
+ # get posting date upto which interest has to be accrued
+ per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
+ self.rate_of_interest, self.posting_date), 2)
- lia = frappe.db.get_value('Loan Interest Accrual', {'process_loan_interest_accrual':
- process}, ['name', 'interest_amount', 'payable_principal_amount'], as_dict=1)
+ no_of_days = flt(flt(self.total_interest_paid - self.interest_payable,
+ precision)/per_day_interest, 0) - 1
- self.append('repayment_details', {
- 'loan_interest_accrual': lia.name,
- 'paid_interest_amount': lia.interest_amount,
- 'paid_principal_amount': lia.payable_principal_amount
- })
+ posting_date = add_days(last_accrual_date, no_of_days)
+
+ # book excess interest paid
+ process = process_loan_interest_accrual_for_demand_loans(posting_date=posting_date,
+ loan=self.against_loan, accrual_type="Repayment")
+
+ # get loan interest accrual to update paid amount
+ lia = frappe.db.get_value('Loan Interest Accrual', {'process_loan_interest_accrual':
+ process}, ['name', 'interest_amount', 'payable_principal_amount'], as_dict=1)
+
+ self.append('repayment_details', {
+ 'loan_interest_accrual': lia.name,
+ 'paid_interest_amount': flt(self.total_interest_paid - self.interest_payable, precision),
+ 'paid_principal_amount': 0.0,
+ 'accrual_type': 'Repayment'
+ })
def update_paid_amount(self):
precision = cint(frappe.db.get_default("currency_precision")) or 2
@@ -108,12 +116,6 @@ class LoanRepayment(AccountsController):
WHERE name = %s""",
(flt(payment.paid_principal_amount, precision), flt(payment.paid_interest_amount, precision), payment.loan_interest_accrual))
- if flt(loan.total_principal_paid + self.principal_amount_paid, precision) >= flt(loan.total_payment, precision):
- if loan.is_secured_loan:
- frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested")
- else:
- frappe.db.set_value("Loan", self.against_loan, "status", "Closed")
-
frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
WHERE name = %s """, (loan.total_amount_paid + self.amount_paid,
loan.total_principal_paid + self.principal_amount_paid, self.against_loan))
@@ -123,6 +125,8 @@ class LoanRepayment(AccountsController):
def mark_as_unpaid(self):
loan = frappe.get_doc("Loan", self.against_loan)
+ no_of_repayments = len(self.repayment_details)
+
for payment in self.repayment_details:
frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
SET paid_principal_amount = `paid_principal_amount` - %s,
@@ -130,6 +134,12 @@ class LoanRepayment(AccountsController):
WHERE name = %s""",
(payment.paid_principal_amount, payment.paid_interest_amount, payment.loan_interest_accrual))
+ # Cancel repayment interest accrual
+ # checking idx as a preventive measure, repayment accrual will always be the last entry
+ if payment.accrual_type == 'Repayment' and payment.idx == no_of_repayments:
+ lia_doc = frappe.get_doc('Loan Interest Accrual', payment.loan_interest_accrual)
+ lia_doc.cancel()
+
frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
WHERE name = %s """, (loan.total_amount_paid - self.amount_paid,
loan.total_principal_paid - self.principal_amount_paid, self.against_loan))
@@ -137,15 +147,17 @@ class LoanRepayment(AccountsController):
if loan.status == "Loan Closure Requested":
frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed")
- def allocate_amounts(self, paid_entries):
+ def allocate_amounts(self, repayment_details):
+ precision = cint(frappe.db.get_default("currency_precision")) or 2
+
self.set('repayment_details', [])
self.principal_amount_paid = 0
total_interest_paid = 0
interest_paid = self.amount_paid - self.penalty_amount
- if self.amount_paid - self.penalty_amount > 0 and paid_entries:
+ if self.amount_paid - self.penalty_amount > 0:
interest_paid = self.amount_paid - self.penalty_amount
- for lia, amounts in iteritems(paid_entries):
+ for lia, amounts in iteritems(repayment_details.get('pending_accrual_entries', [])):
if amounts['interest_amount'] + amounts['payable_principal_amount'] <= interest_paid:
interest_amount = amounts['interest_amount']
paid_principal = amounts['payable_principal_amount']
@@ -169,9 +181,24 @@ class LoanRepayment(AccountsController):
'paid_principal_amount': paid_principal
})
- if self.payment_type == 'Loan Closure' and total_interest_paid < self.interest_payable:
- unaccrued_interest = self.interest_payable - total_interest_paid
- interest_paid -= unaccrued_interest
+ if repayment_details['unaccrued_interest'] and interest_paid:
+ # no of days for which to accrue interest
+ # Interest can only be accrued for an entire day and not partial
+ if interest_paid > repayment_details['unaccrued_interest']:
+ per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
+ self.rate_of_interest, self.posting_date), precision)
+ interest_paid -= repayment_details['unaccrued_interest']
+ total_interest_paid += repayment_details['unaccrued_interest']
+ else:
+ # get no of days for which interest can be paid
+ per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
+ self.rate_of_interest, self.posting_date), precision)
+
+ no_of_days = cint(interest_paid/per_day_interest)
+ total_interest_paid += no_of_days * per_day_interest
+ interest_paid -= no_of_days * per_day_interest
+
+ self.total_interest_paid = total_interest_paid
if interest_paid:
self.principal_amount_paid += interest_paid
@@ -189,7 +216,7 @@ class LoanRepayment(AccountsController):
"debit_in_account_currency": self.penalty_amount,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
- "remarks": _("Against Loan:") + self.against_loan,
+ "remarks": _("Penalty against loan:") + self.against_loan,
"cost_center": self.cost_center,
"party_type": self.applicant_type,
"party": self.applicant,
@@ -205,10 +232,8 @@ class LoanRepayment(AccountsController):
"credit_in_account_currency": self.penalty_amount,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
- "remarks": _("Against Loan:") + self.against_loan,
+ "remarks": _("Penalty against loan:") + self.against_loan,
"cost_center": self.cost_center,
- "party_type": self.applicant_type,
- "party": self.applicant,
"posting_date": getdate(self.posting_date)
})
)
@@ -219,13 +244,11 @@ class LoanRepayment(AccountsController):
"against": loan_details.loan_account + ", " + loan_details.interest_income_account
+ ", " + loan_details.penalty_income_account,
"debit": self.amount_paid,
- "debit_in_account_currency": self.amount_paid ,
+ "debit_in_account_currency": self.amount_paid,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
- "remarks": _("Against Loan:") + self.against_loan,
+ "remarks": _("Repayment against Loan: ") + self.against_loan,
"cost_center": self.cost_center,
- "party_type": self.applicant_type,
- "party": self.applicant,
"posting_date": getdate(self.posting_date)
})
)
@@ -240,7 +263,7 @@ class LoanRepayment(AccountsController):
"credit_in_account_currency": self.amount_paid,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
- "remarks": _("Against Loan:") + self.against_loan,
+ "remarks": _("Repayment against Loan: ") + self.against_loan,
"cost_center": self.cost_center,
"posting_date": getdate(self.posting_date)
})
@@ -273,7 +296,8 @@ def get_accrued_interest_entries(against_loan):
unpaid_accrued_entries = frappe.db.sql(
"""
SELECT name, posting_date, interest_amount - paid_interest_amount as interest_amount,
- payable_principal_amount - paid_principal_amount as payable_principal_amount
+ payable_principal_amount - paid_principal_amount as payable_principal_amount,
+ accrual_type
FROM
`tabLoan Interest Accrual`
WHERE
@@ -282,6 +306,7 @@ def get_accrued_interest_entries(against_loan):
payable_principal_amount - paid_principal_amount > 0)
AND
docstatus = 1
+ ORDER BY posting_date
""", (against_loan), as_dict=1)
return unpaid_accrued_entries
@@ -289,7 +314,7 @@ def get_accrued_interest_entries(against_loan):
# This function returns the amounts that are payable at the time of loan repayment based on posting date
# So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
-def get_amounts(amounts, against_loan, posting_date, payment_type):
+def get_amounts(amounts, against_loan, posting_date):
precision = cint(frappe.db.get_default("currency_precision")) or 2
against_loan_doc = frappe.get_doc("Loan", against_loan)
@@ -311,10 +336,10 @@ def get_amounts(amounts, against_loan, posting_date, payment_type):
due_date = add_days(entry.posting_date, 1)
no_of_late_days = date_diff(posting_date,
- add_days(due_date, loan_type_details.grace_period_in_days))
+ add_days(due_date, loan_type_details.grace_period_in_days)) + 1
- if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary):
- penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)/365
+ if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary) and entry.accrual_type == 'Regular':
+ penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)
total_pending_interest += entry.interest_amount
payable_principal_amount += entry.payable_principal_amount
@@ -324,23 +349,27 @@ def get_amounts(amounts, against_loan, posting_date, payment_type):
'payable_principal_amount': flt(entry.payable_principal_amount, precision)
})
- if not final_due_date:
+ if due_date and not final_due_date:
final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
if against_loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'):
- pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid - against_loan_doc.total_interest_payable
+ pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid \
+ - against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
else:
- pending_principal_amount = against_loan_doc.disbursed_amount
+ pending_principal_amount = against_loan_doc.disbursed_amount - against_loan_doc.total_principal_paid \
+ - against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
- if payment_type == "Loan Closure":
- if due_date:
- pending_days = date_diff(posting_date, due_date) + 1
- else:
- pending_days = date_diff(posting_date, against_loan_doc.disbursement_date) + 1
+ unaccrued_interest = 0
+ if due_date:
+ pending_days = date_diff(posting_date, due_date) + 1
+ else:
+ last_accrual_date = get_last_accrual_date(against_loan_doc.name)
+ pending_days = date_diff(posting_date, last_accrual_date) + 1
- payable_principal_amount = pending_principal_amount
- per_day_interest = (payable_principal_amount * (loan_type_details.rate_of_interest / 100))/365
- total_pending_interest += (pending_days * per_day_interest)
+ if pending_days > 0:
+ principal_amount = flt(pending_principal_amount, precision)
+ per_day_interest = get_per_day_interest(principal_amount, loan_type_details.rate_of_interest, posting_date)
+ unaccrued_interest += (pending_days * flt(per_day_interest, precision))
amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
@@ -348,6 +377,7 @@ def get_amounts(amounts, against_loan, posting_date, payment_type):
amounts["penalty_amount"] = flt(penalty_amount, precision)
amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
amounts["pending_accrual_entries"] = pending_accrual_entries
+ amounts["unaccrued_interest"] = unaccrued_interest
if final_due_date:
amounts["due_date"] = final_due_date
@@ -355,7 +385,7 @@ def get_amounts(amounts, against_loan, posting_date, payment_type):
return amounts
@frappe.whitelist()
-def calculate_amounts(against_loan, posting_date, payment_type):
+def calculate_amounts(against_loan, posting_date, payment_type=''):
amounts = {
'penalty_amount': 0.0,
@@ -363,10 +393,17 @@ def calculate_amounts(against_loan, posting_date, payment_type):
'pending_principal_amount': 0.0,
'payable_principal_amount': 0.0,
'payable_amount': 0.0,
+ 'unaccrued_interest': 0.0,
'due_date': ''
}
- amounts = get_amounts(amounts, against_loan, posting_date, payment_type)
+ amounts = get_amounts(amounts, against_loan, posting_date)
+
+ # update values for closure
+ if payment_type == 'Loan Closure':
+ amounts['payable_principal_amount'] = amounts['pending_principal_amount']
+ amounts['interest_amount'] += amounts['unaccrued_interest']
+ amounts['payable_amount'] = amounts['payable_principal_amount'] + amounts['interest_amount']
return amounts
diff --git a/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.json b/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.json
index cff1dbb1d2..4b9b191e26 100644
--- a/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.json
+++ b/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.json
@@ -7,7 +7,8 @@
"field_order": [
"loan_interest_accrual",
"paid_principal_amount",
- "paid_interest_amount"
+ "paid_interest_amount",
+ "accrual_type"
],
"fields": [
{
@@ -27,11 +28,20 @@
"fieldtype": "Currency",
"label": "Paid Interest Amount",
"options": "Company:company:default_currency"
+ },
+ {
+ "fetch_from": "loan_interest_accrual.accrual_type",
+ "fetch_if_empty": 1,
+ "fieldname": "accrual_type",
+ "fieldtype": "Select",
+ "label": "Accrual Type",
+ "options": "Regular\nRepayment\nDisbursement"
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-15 21:50:03.837019",
+ "modified": "2020-10-23 08:09:18.267030",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Repayment Detail",
diff --git a/erpnext/loan_management/doctype/loan_security/loan_security.json b/erpnext/loan_management/doctype/loan_security/loan_security.json
index 1d0bb30910..c698601ea4 100644
--- a/erpnext/loan_management/doctype/loan_security/loan_security.json
+++ b/erpnext/loan_management/doctype/loan_security/loan_security.json
@@ -25,6 +25,7 @@
},
{
"fetch_from": "loan_security_type.haircut",
+ "fetch_if_empty": 1,
"fieldname": "haircut",
"fieldtype": "Percent",
"label": "Haircut %"
@@ -64,8 +65,9 @@
"reqd": 1
}
],
+ "index_web_pages_for_search": 1,
"links": [],
- "modified": "2020-04-29 13:21:26.043492",
+ "modified": "2020-10-26 07:34:48.601766",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Security",
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
index 2bb6fd84e5..cbc8376aa5 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
@@ -78,7 +78,7 @@ class LoanSecurityPledge(Document):
self.maximum_loan_value = maximum_loan_value
def update_loan(loan, maximum_value_against_pledge):
- maximum_loan_value = frappe.db.get_value('Loan', {'name': loan}, ['maximum_loan_value'])
+ maximum_loan_value = frappe.db.get_value('Loan', {'name': loan}, ['maximum_loan_amount'])
- frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_value=%s, is_secured_loan=1
+ frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_amount=%s, is_secured_loan=1
WHERE name=%s""", (maximum_loan_value + maximum_value_against_pledge, loan))
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
index 0f42bde3c4..8ec0bfb62c 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
@@ -22,7 +22,7 @@ def update_shortfall_status(loan, security_value):
if security_value >= loan_security_shortfall.shortfall_amount:
frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, {
"status": "Completed",
- "shortfall_value": loan_security_shortfall.shortfall_amount})
+ "shortfall_amount": loan_security_shortfall.shortfall_amount})
else:
frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name,
"shortfall_amount", loan_security_shortfall.shortfall_amount - security_value)
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
index b3eb6001e4..c29f325bfc 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
@@ -42,18 +42,20 @@ class LoanSecurityUnpledge(Document):
"valid_upto": (">=", get_datetime())
}, as_list=1))
- total_payment, principal_paid, interest_payable = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
- 'total_interest_payable'])
+ total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
+ 'total_interest_payable', 'written_off_amount'])
- pending_principal_amount = flt(total_payment) - flt(interest_payable) - flt(principal_paid)
+ pending_principal_amount = flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount)
security_value = 0
for security in self.securities:
pledged_qty = pledge_qty_map.get(security.loan_security, 0)
if security.qty > pledged_qty:
- frappe.throw(_("""Row {0}: {1} {2} of {3} is pledged against Loan {4}.
- You are trying to unpledge more""").format(security.idx, pledged_qty, security.uom,
- frappe.bold(security.loan_security), frappe.bold(self.loan)))
+ msg = _("Row {0}: {1} {2} of {3} is pledged against Loan {4}.").format(security.idx, pledged_qty, security.uom,
+ frappe.bold(security.loan_security), frappe.bold(self.loan))
+ msg += "
"
+ msg += _("You are trying to unpledge more.")
+ frappe.throw(msg, title=_("Loan Security Unpledge Error"))
qty_after_unpledge = pledged_qty - security.qty
ltv_ratio = ltv_ratio_map.get(security.loan_security_type)
@@ -65,10 +67,18 @@ class LoanSecurityUnpledge(Document):
security_value += qty_after_unpledge * current_price
if not security_value and flt(pending_principal_amount, 2) > 0:
- frappe.throw("Cannot Unpledge, loan to value ratio is breaching")
+ self._throw(security_value, pending_principal_amount, ltv_ratio)
if security_value and flt(pending_principal_amount/security_value) * 100 > ltv_ratio:
- frappe.throw("Cannot Unpledge, loan to value ratio is breaching")
+ self._throw(security_value, pending_principal_amount, ltv_ratio)
+
+ def _throw(self, security_value, pending_principal_amount, ltv_ratio):
+ msg = _("Loan Security Value after unpledge is {0}").format(frappe.bold(security_value))
+ msg += '
'
+ msg += _("Pending principal amount is {0}").format(frappe.bold(flt(pending_principal_amount, 2)))
+ msg += '
'
+ msg += _("Loan To Security Value ratio must always be {0}").format(frappe.bold(ltv_ratio))
+ frappe.throw(msg, title=_("Loan To Value ratio breach"))
def on_update_after_submit(self):
self.approve()
diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json
index 669490a448..18a97315f0 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type.json
+++ b/erpnext/loan_management/doctype/loan_type/loan_type.json
@@ -11,6 +11,7 @@
"rate_of_interest",
"penalty_interest_rate",
"grace_period_in_days",
+ "write_off_amount",
"column_break_2",
"company",
"is_term_loan",
@@ -76,7 +77,6 @@
"reqd": 1
},
{
- "description": "This account is used for booking loan repayments from the borrower and also disbursing loans to the borrower",
"fieldname": "payment_account",
"fieldtype": "Link",
"label": "Payment Account",
@@ -84,7 +84,6 @@
"reqd": 1
},
{
- "description": "This account is capital account which is used to allocate capital for loan disbursal account ",
"fieldname": "loan_account",
"fieldtype": "Link",
"label": "Loan Account",
@@ -96,7 +95,6 @@
"fieldtype": "Column Break"
},
{
- "description": "This account will be used for booking loan interest accruals",
"fieldname": "interest_income_account",
"fieldtype": "Link",
"label": "Interest Income Account",
@@ -104,7 +102,6 @@
"reqd": 1
},
{
- "description": "This account will be used for booking penalties levied due to delayed repayments",
"fieldname": "penalty_income_account",
"fieldtype": "Link",
"label": "Penalty Income Account",
@@ -113,7 +110,6 @@
},
{
"default": "0",
- "description": "If this is not checked the loan by default will be considered as a Demand Loan",
"fieldname": "is_term_loan",
"fieldtype": "Check",
"label": "Is Term Loan"
@@ -145,17 +141,27 @@
"label": "Company",
"options": "Company",
"reqd": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "description": "Pending amount that will be automatically ignored on loan closure request ",
+ "fieldname": "write_off_amount",
+ "fieldtype": "Currency",
+ "label": "Write Off Amount ",
+ "options": "Company:company:default_currency"
}
],
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-06-07 18:55:59.346292",
+ "modified": "2020-10-26 07:13:55.029811",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Type",
"owner": "Administrator",
"permissions": [
{
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -165,6 +171,7 @@
"report": 1,
"role": "Loan Manager",
"share": 1,
+ "submit": 1,
"write": 1
},
{
diff --git a/erpnext/loan_management/doctype/loan_write_off/__init__.py b/erpnext/loan_management/doctype/loan_write_off/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.js b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.js
new file mode 100644
index 0000000000..4e3319c208
--- /dev/null
+++ b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.js
@@ -0,0 +1,36 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+{% include 'erpnext/loan_management/loan_common.js' %};
+
+frappe.ui.form.on('Loan Write Off', {
+ loan: function(frm) {
+ frm.trigger('show_pending_principal_amount');
+ },
+ onload: function(frm) {
+ frm.trigger('show_pending_principal_amount');
+ },
+ refresh: function(frm) {
+ frm.set_query('write_off_account', function(){
+ return {
+ filters: {
+ 'company': frm.doc.company,
+ 'root_type': 'Expense',
+ 'is_group': 0
+ }
+ }
+ });
+ },
+ show_pending_principal_amount: function(frm) {
+ if (frm.doc.loan && frm.doc.docstatus === 0) {
+ frappe.db.get_value('Loan', frm.doc.loan, ['total_payment', 'total_interest_payable',
+ 'total_principal_paid', 'written_off_amount'], function(values) {
+ frm.set_df_property('write_off_amount', 'description',
+ "Pending principal amount is " + cstr(flt(values.total_payment - values.total_interest_payable
+ - values.total_principal_paid - values.written_off_amount, 2)));
+ frm.refresh_field('write_off_amount');
+ });
+
+ }
+ }
+});
diff --git a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.json b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.json
new file mode 100644
index 0000000000..4617a62f5b
--- /dev/null
+++ b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.json
@@ -0,0 +1,157 @@
+{
+ "actions": [],
+ "autoname": "LM-WO-.#####",
+ "creation": "2020-10-16 11:09:14.495066",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "loan",
+ "applicant_type",
+ "applicant",
+ "column_break_3",
+ "company",
+ "posting_date",
+ "accounting_dimensions_section",
+ "cost_center",
+ "section_break_9",
+ "write_off_account",
+ "column_break_11",
+ "write_off_amount",
+ "amended_from"
+ ],
+ "fields": [
+ {
+ "fieldname": "loan",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Loan",
+ "options": "Loan",
+ "reqd": 1
+ },
+ {
+ "default": "Today",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Posting Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "loan.company",
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Company",
+ "options": "Company",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fetch_from": "loan.applicant_type",
+ "fieldname": "applicant_type",
+ "fieldtype": "Select",
+ "label": "Applicant Type",
+ "options": "Employee\nMember\nCustomer",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "loan.applicant",
+ "fieldname": "applicant",
+ "fieldtype": "Dynamic Link",
+ "label": "Applicant ",
+ "options": "applicant_type",
+ "read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "label": "Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname": "section_break_9",
+ "fieldtype": "Section Break",
+ "label": "Write Off Details"
+ },
+ {
+ "fieldname": "write_off_account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Write Off Account",
+ "options": "Account",
+ "reqd": 1
+ },
+ {
+ "fieldname": "write_off_amount",
+ "fieldtype": "Currency",
+ "label": "Write Off Amount",
+ "options": "Company:company:default_currency",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Loan Write Off",
+ "print_hide": 1,
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-10-26 07:13:43.663924",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loan Write Off",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Loan Manager",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py
new file mode 100644
index 0000000000..54a3f2cbb1
--- /dev/null
+++ b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, erpnext
+from frappe import _
+from frappe.utils import getdate, flt, cint
+from erpnext.controllers.accounts_controller import AccountsController
+from erpnext.accounts.general_ledger import make_gl_entries
+
+class LoanWriteOff(AccountsController):
+ def validate(self):
+ self.set_missing_values()
+ self.validate_write_off_amount()
+
+ def set_missing_values(self):
+ if not self.cost_center:
+ self.cost_center = erpnext.get_default_cost_center(self.company)
+
+ def validate_write_off_amount(self):
+ precision = cint(frappe.db.get_default("currency_precision")) or 2
+ total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value("Loan", self.loan,
+ ['total_payment', 'total_principal_paid','total_interest_payable', 'written_off_amount'])
+
+ pending_principal_amount = flt(flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount),
+ precision)
+
+ if self.write_off_amount > pending_principal_amount:
+ frappe.throw(_("Write off amount cannot be greater than pending principal amount"))
+
+ def on_submit(self):
+ self.update_outstanding_amount()
+ self.make_gl_entries()
+
+ def on_cancel(self):
+ self.update_outstanding_amount(cancel=1)
+ self.ignore_linked_doctypes = ['GL Entry']
+ self.make_gl_entries(cancel=1)
+
+ def update_outstanding_amount(self, cancel=0):
+ written_off_amount = frappe.db.get_value('Loan', self.loan, 'written_off_amount')
+
+ if cancel:
+ written_off_amount -= self.write_off_amount
+ else:
+ written_off_amount += self.write_off_amount
+
+ frappe.db.set_value('Loan', self.loan, 'written_off_amount', written_off_amount)
+
+
+ def make_gl_entries(self, cancel=0):
+ gl_entries = []
+ loan_details = frappe.get_doc("Loan", self.loan)
+
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": self.write_off_account,
+ "against": loan_details.loan_account,
+ "debit": self.write_off_amount,
+ "debit_in_account_currency": self.write_off_amount,
+ "against_voucher_type": "Loan",
+ "against_voucher": self.loan,
+ "remarks": _("Against Loan:") + self.loan,
+ "cost_center": self.cost_center,
+ "posting_date": getdate(self.posting_date)
+ })
+ )
+
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": loan_details.loan_account,
+ "party_type": loan_details.applicant_type,
+ "party": loan_details.applicant,
+ "against": self.write_off_account,
+ "credit": self.write_off_amount,
+ "credit_in_account_currency": self.write_off_amount,
+ "against_voucher_type": "Loan",
+ "against_voucher": self.loan,
+ "remarks": _("Against Loan:") + self.loan,
+ "cost_center": self.cost_center,
+ "posting_date": getdate(self.posting_date)
+ })
+ )
+
+ make_gl_entries(gl_entries, cancel=cancel, merge_entries=False)
+
+
diff --git a/erpnext/loan_management/doctype/loan_write_off/test_loan_write_off.py b/erpnext/loan_management/doctype/loan_write_off/test_loan_write_off.py
new file mode 100644
index 0000000000..9f6700e274
--- /dev/null
+++ b/erpnext/loan_management/doctype/loan_write_off/test_loan_write_off.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestLoanWriteOff(unittest.TestCase):
+ pass
diff --git a/erpnext/loan_management/doctype/pledge/pledge.json b/erpnext/loan_management/doctype/pledge/pledge.json
index f22a21e3be..801e3a3117 100644
--- a/erpnext/loan_management/doctype/pledge/pledge.json
+++ b/erpnext/loan_management/doctype/pledge/pledge.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2019-09-09 17:06:16.756573",
"doctype": "DocType",
"editable_grid": 1,
@@ -49,7 +50,8 @@
"fieldname": "qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Quantity"
+ "label": "Quantity",
+ "non_negative": 1
},
{
"fieldname": "loan_security_price",
@@ -86,7 +88,8 @@
}
],
"istable": 1,
- "modified": "2019-12-03 10:59:58.001421",
+ "links": [],
+ "modified": "2020-11-05 10:07:15.424937",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Pledge",
diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json
index 0ef098f278..828df2e35f 100644
--- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json
+++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json
@@ -10,6 +10,7 @@
"loan_type",
"loan",
"process_type",
+ "accrual_type",
"amended_from"
],
"fields": [
@@ -47,17 +48,27 @@
"hidden": 1,
"label": "Process Type",
"read_only": 1
+ },
+ {
+ "fieldname": "accrual_type",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "label": "Accrual Type",
+ "options": "Regular\nRepayment\nDisbursement",
+ "read_only": 1
}
],
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-04-09 22:52:53.911416",
+ "modified": "2020-11-06 13:28:51.478909",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Process Loan Interest Accrual",
"owner": "Administrator",
"permissions": [
{
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -67,9 +78,11 @@
"report": 1,
"role": "System Manager",
"share": 1,
+ "submit": 1,
"write": 1
},
{
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -79,6 +92,7 @@
"report": 1,
"role": "Loan Manager",
"share": 1,
+ "submit": 1,
"write": 1
}
],
diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
index 0fa96860d0..11333dc2aa 100644
--- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
@@ -20,19 +20,20 @@ class ProcessLoanInterestAccrual(Document):
if (not self.loan or not loan_doc.is_term_loan) and self.process_type != 'Term Loans':
make_accrual_interest_entry_for_demand_loans(self.posting_date, self.name,
- open_loans = open_loans, loan_type = self.loan_type)
+ open_loans = open_loans, loan_type = self.loan_type, accrual_type=self.accrual_type)
if (not self.loan or loan_doc.is_term_loan) and self.process_type != 'Demand Loans':
make_accrual_interest_entry_for_term_loans(self.posting_date, self.name, term_loan=self.loan,
- loan_type=self.loan_type)
+ loan_type=self.loan_type, accrual_type=self.accrual_type)
-def process_loan_interest_accrual_for_demand_loans(posting_date=None, loan_type=None, loan=None):
+def process_loan_interest_accrual_for_demand_loans(posting_date=None, loan_type=None, loan=None, accrual_type="Regular"):
loan_process = frappe.new_doc('Process Loan Interest Accrual')
loan_process.posting_date = posting_date or nowdate()
loan_process.loan_type = loan_type
loan_process.process_type = 'Demand Loans'
loan_process.loan = loan
+ loan_process.accrual_type = accrual_type
loan_process.submit()
diff --git a/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.json b/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.json
index aee7c2ced5..3e7e778a25 100644
--- a/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.json
+++ b/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2019-08-29 22:29:37.628178",
"doctype": "DocType",
"editable_grid": 1,
@@ -39,7 +40,8 @@
"fieldname": "qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Quantity"
+ "label": "Quantity",
+ "non_negative": 1
},
{
"fieldname": "loan_security",
@@ -56,8 +58,10 @@
"read_only": 1
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
- "modified": "2019-12-02 10:23:11.498308",
+ "links": [],
+ "modified": "2020-11-05 10:07:37.542344",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Proposed Pledge",
diff --git a/erpnext/loan_management/doctype/unpledge/unpledge.json b/erpnext/loan_management/doctype/unpledge/unpledge.json
index ee192d7377..00356685eb 100644
--- a/erpnext/loan_management/doctype/unpledge/unpledge.json
+++ b/erpnext/loan_management/doctype/unpledge/unpledge.json
@@ -52,6 +52,7 @@
"fieldtype": "Float",
"in_list_view": 1,
"label": "Quantity",
+ "non_negative": 1,
"reqd": 1
},
{
@@ -62,9 +63,10 @@
"read_only": 1
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-05-06 10:50:18.448552",
+ "modified": "2020-11-05 10:07:28.106961",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Unpledge",
diff --git a/erpnext/loan_management/loan_common.js b/erpnext/loan_management/loan_common.js
index d9dd415296..50b68da30e 100644
--- a/erpnext/loan_management/loan_common.js
+++ b/erpnext/loan_management/loan_common.js
@@ -8,14 +8,14 @@ frappe.ui.form.on(cur_frm.doctype, {
frm.refresh_field('applicant_type');
}
- if (['Loan Disbursement', 'Loan Repayment', 'Loan Interest Accrual'].includes(frm.doc.doctype)
+ if (['Loan Disbursement', 'Loan Repayment', 'Loan Interest Accrual', 'Loan Write Off'].includes(frm.doc.doctype)
&& frm.doc.docstatus > 0) {
frm.add_custom_button(__("Accounting Ledger"), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
company: frm.doc.company,
- from_date: frm.doc.posting_date,
+ from_date: moment(frm.doc.posting_date).format('YYYY-MM-DD'),
to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
show_cancelled_entries: frm.doc.docstatus === 2
};
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 2ab1b98707..8888a96768 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -76,6 +76,7 @@ class BOM(WebsiteGenerator):
self.set_routing_operations()
self.validate_operations()
self.calculate_cost()
+ self.update_stock_qty()
self.update_cost(update_parent=False, from_child_bom=True, save=False)
def get_context(self, context):
@@ -84,8 +85,6 @@ class BOM(WebsiteGenerator):
def on_update(self):
frappe.cache().hdel('bom_children', self.name)
self.check_recursion()
- self.update_stock_qty()
- self.update_exploded_items()
def on_submit(self):
self.manage_default_bom()
@@ -237,7 +236,8 @@ class BOM(WebsiteGenerator):
self.calculate_cost()
if save:
self.db_update()
- self.update_exploded_items()
+
+ self.update_exploded_items(save=save)
# update parent BOMs
if self.total_cost != existing_bom_cost and update_parent:
@@ -318,8 +318,6 @@ class BOM(WebsiteGenerator):
m.uom = m.stock_uom
m.qty = m.stock_qty
- m.db_update()
-
def validate_uom_is_interger(self):
from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "uom", "qty", "BOM Item")
@@ -372,15 +370,6 @@ class BOM(WebsiteGenerator):
if raise_exception:
frappe.throw(_("BOM recursion: {0} cannot be parent or child of {1}").format(self.name, self.name))
- def update_cost_and_exploded_items(self, bom_list=[]):
- bom_list = self.traverse_tree(bom_list)
- for bom in bom_list:
- bom_obj = frappe.get_doc("BOM", bom)
- bom_obj.check_recursion(bom_list=bom_list)
- bom_obj.update_exploded_items()
-
- return bom_list
-
def traverse_tree(self, bom_list=None):
def _get_children(bom_no):
children = frappe.cache().hget('bom_children', bom_no)
@@ -472,10 +461,10 @@ class BOM(WebsiteGenerator):
d.rate = rate
d.amount = (d.stock_qty or d.qty) * rate
- def update_exploded_items(self):
+ def update_exploded_items(self, save=True):
""" Update Flat BOM, following will be correct data"""
self.get_exploded_items()
- self.add_exploded_items()
+ self.add_exploded_items(save=save)
def get_exploded_items(self):
""" Get all raw materials including items from child bom"""
@@ -544,11 +533,13 @@ class BOM(WebsiteGenerator):
'sourced_by_supplier': d.get('sourced_by_supplier', 0)
}))
- def add_exploded_items(self):
+ def add_exploded_items(self, save=True):
"Add items to Flat BOM table"
- frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name)
self.set('exploded_items', [])
+ if save:
+ frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name)
+
for d in sorted(self.cur_exploded_items, key=itemgetter(0)):
ch = self.append('exploded_items', {})
for i in self.cur_exploded_items[d].keys():
@@ -556,7 +547,9 @@ class BOM(WebsiteGenerator):
ch.amount = flt(ch.stock_qty) * flt(ch.rate)
ch.qty_consumed_per_unit = flt(ch.stock_qty) / flt(self.quantity)
ch.docstatus = self.docstatus
- ch.db_insert()
+
+ if save:
+ ch.db_insert()
def validate_bom_links(self):
if not self.is_active:
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 850d5aeff8..7daf7069f3 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -19,6 +19,7 @@
"column_break2",
"from_date",
"to_date",
+ "sales_order_status",
"sales_orders_detail",
"get_sales_orders",
"sales_orders",
@@ -301,13 +302,20 @@
"label": "Warehouses",
"options": "Production Plan Material Request Warehouse",
"read_only": 1
+ },
+ {
+ "depends_on": "eval: doc.get_items_from == \"Sales Order\"",
+ "fieldname": "sales_order_status",
+ "fieldtype": "Select",
+ "label": "Sales Order Status",
+ "options": "\nTo Deliver and Bill\nTo Bill\nTo Deliver"
}
],
"icon": "fa fa-calendar",
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-26 13:00:54.335319",
+ "modified": "2020-11-10 18:01:54.991970",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index a314a15c23..3833e86d27 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -571,6 +571,8 @@ def get_sales_orders(self):
so_filter += " and so.customer = %(customer)s"
if self.project:
so_filter += " and so.project = %(project)s"
+ if self.sales_order_status:
+ so_filter += "and so.status = %(sales_order_status)s"
if self.item_code:
item_filter += " and so_item.item_code = %(item)s"
@@ -594,8 +596,8 @@ def get_sales_orders(self):
"customer": self.customer,
"project": self.project,
"item": self.item_code,
- "company": self.company
-
+ "company": self.company,
+ "sales_order_status": self.sales_order_status
}, as_dict=1)
return open_so
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index fa9d080cca..27335aa204 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -137,7 +137,8 @@ class TestProductionPlan(unittest.TestCase):
'from_date': so.transaction_date,
'to_date': so.transaction_date,
'customer': so.customer,
- 'item_code': item
+ 'item_code': item,
+ 'sales_order_status': so.status
})
sales_orders = get_sales_orders(pln) or {}
sales_orders = [d.get('name') for d in sales_orders if d.get('name') == sales_order]
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 5f8a13428c..e53927918e 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -443,6 +443,11 @@ class TestWorkOrder(unittest.TestCase):
ste1 = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 1))
self.assertEqual(len(ste1.items), 3)
+ def test_cost_center_for_manufacture(self):
+ wo_order = make_wo_order_test_record()
+ ste = make_stock_entry(wo_order.name, "Material Transfer for Manufacture", wo_order.qty)
+ self.assertEquals(ste.get("items")[0].get("cost_center"), "_Test Cost Center - _TC")
+
def test_operation_time_with_batch_size(self):
fg_item = "Test Batch Size Item For BOM"
rm1 = "Test Batch Size Item RM 1 For BOM"
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index 4c85cb60e8..7d15abaa3b 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -70,7 +70,7 @@ class Membership(Document):
settings = frappe.get_doc("Membership Settings")
if not member.customer:
- frappe.throw(_("No customer linked to member {}", [member.name]))
+ frappe.throw(_("No customer linked to member {0}").format(frappe.bold(self.member)))
if not settings.debit_account:
frappe.throw(_("You need to set Debit Account in Membership Settings"))
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 979ba98300..b7fcbb1339 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -733,5 +733,6 @@ erpnext.patches.v13_0.print_uom_after_quantity_patch
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
+erpnext.patches.v13_0.update_custom_fields_for_shopify
execute:frappe.delete_doc("Report", "Quoted Item Comparison")
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
index 7afde373c3..fe072d7eb9 100644
--- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
@@ -51,7 +51,7 @@ def generate_encashment_leave_ledger_entries():
for encashment in leave_encashments:
if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}):
- frappe.get_doc("Leave Enchashment", encashment).create_leave_ledger_entry()
+ frappe.get_doc("Leave Encashment", encashment).create_leave_ledger_entry()
def generate_expiry_allocation_ledger_entries():
''' fix ledger entries for missing leave allocation transaction '''
diff --git a/erpnext/patches/v13_0/update_custom_fields_for_shopify.py b/erpnext/patches/v13_0/update_custom_fields_for_shopify.py
new file mode 100644
index 0000000000..f1d2ea2d74
--- /dev/null
+++ b/erpnext/patches/v13_0/update_custom_fields_for_shopify.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import setup_custom_fields
+
+def execute():
+ if frappe.db.get_single_value('Shopify Settings', 'enable_shopify'):
+ setup_custom_fields()
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 30ea432678..a3d12c35c0 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -344,9 +344,13 @@ class PayrollEntry(Document):
employees_to_mark_attendance = []
days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
for employee_detail in self.employees:
- days_holiday = self.get_count_holidays_of_employee(employee_detail.employee)
- days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee)
- days_in_payroll = date_diff(self.end_date, self.start_date) + 1
+ employee_joining_date = frappe.db.get_value("Employee", employee_detail.employee, 'date_of_joining')
+ start_date = self.start_date
+ if employee_joining_date > getdate(self.start_date):
+ start_date = employee_joining_date
+ days_holiday = self.get_count_holidays_of_employee(employee_detail.employee, start_date)
+ days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee, start_date)
+ days_in_payroll = date_diff(self.end_date, start_date) + 1
if days_in_payroll > days_holiday + days_attendance_marked:
employees_to_mark_attendance.append({
"employee": employee_detail.employee,
@@ -354,22 +358,25 @@ class PayrollEntry(Document):
})
return employees_to_mark_attendance
- def get_count_holidays_of_employee(self, employee):
+ def get_count_holidays_of_employee(self, employee, start_date):
holiday_list = get_holiday_list_for_employee(employee)
holidays = 0
if holiday_list:
days = frappe.db.sql("""select count(*) from tabHoliday where
parent=%s and holiday_date between %s and %s""", (holiday_list,
- self.start_date, self.end_date))
+ start_date, self.end_date))
if days and days[0][0]:
holidays = days[0][0]
return holidays
- def get_count_employee_attendance(self, employee):
+ def get_count_employee_attendance(self, employee, start_date):
marked_days = 0
- attendances = frappe.db.sql("""select count(*) from tabAttendance where
- employee=%s and docstatus=1 and attendance_date between %s and %s""",
- (employee, self.start_date, self.end_date))
+ attendances = frappe.get_all("Attendance",
+ fields = ["count(*)"],
+ filters = {
+ "employee": employee,
+ "attendance_date": ('between', [start_date, self.end_date])
+ }, as_list=1)
if attendances and attendances[0][0]:
marked_days = attendances[0][0]
return marked_days
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 4ccf56435d..cecb8cde7c 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -240,7 +240,6 @@ class SalarySlip(TransactionBase):
self.absent_days += unmarked_days #will be treated as absent
self.payment_days -= unmarked_days
if include_holidays_in_total_working_days:
- self.absent_days -= len(holidays)
for holiday in holidays:
if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }):
self.payment_days += 1
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py
index 4b052bf5c4..500543ceb0 100644
--- a/erpnext/payroll/report/bank_remittance/bank_remittance.py
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py
@@ -47,33 +47,39 @@ def execute(filters=None):
"fieldtype": "Int",
"fieldname": "employee_account_no",
"width": 50
- },
- {
+ }
+ ]
+
+ if frappe.db.has_column('Employee', 'ifsc_code'):
+ columns.append({
"label": _("IFSC Code"),
"fieldtype": "Data",
"fieldname": "bank_code",
"width": 100
- },
- {
- "label": _("Currency"),
- "fieldtype": "Data",
- "fieldname": "currency",
- "width": 50
- },
- {
- "label": _("Net Salary Amount"),
- "fieldtype": "Currency",
- "options": "currency",
- "fieldname": "amount",
- "width": 100
- }
- ]
+ })
+
+ columns += [{
+ "label": _("Currency"),
+ "fieldtype": "Data",
+ "fieldname": "currency",
+ "width": 50
+ },
+ {
+ "label": _("Net Salary Amount"),
+ "fieldtype": "Currency",
+ "options": "currency",
+ "fieldname": "amount",
+ "width": 100
+ }]
+
data = []
accounts = get_bank_accounts()
payroll_entries = get_payroll_entries(accounts, filters)
salary_slips = get_salary_slips(payroll_entries)
- get_emp_bank_ifsc_code(salary_slips)
+
+ if frappe.db.has_column('Employee', 'ifsc_code'):
+ get_emp_bank_ifsc_code(salary_slips)
for salary in salary_slips:
if salary.bank_name and salary.bank_account_no and salary.debit_acc_no and salary.status in ["Submitted", "Paid"]:
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 607c3fd974..b068245a8b 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -133,6 +133,11 @@ frappe.ui.form.on("Timesheet", {
frm: frm
});
},
+
+ project: function(frm) {
+ set_project_in_timelog(frm);
+ },
+
});
frappe.ui.form.on("Timesheet Detail", {
@@ -162,7 +167,11 @@ frappe.ui.form.on("Timesheet Detail", {
frappe.model.set_value(cdt, cdn, "hours", hours);
},
- time_logs_add: function(frm) {
+ time_logs_add: function(frm, cdt, cdn) {
+ if(frm.doc.project) {
+ frappe.model.set_value(cdt, cdn, 'project', frm.doc.project);
+ }
+
var $trigger_again = $('.form-grid').find('.grid-row').find('.btn-open-row');
$trigger_again.on('click', () => {
$('.form-grid')
@@ -297,3 +306,9 @@ const set_employee_and_company = function(frm) {
}
});
};
+
+function set_project_in_timelog(frm) {
+ if(frm.doc.project){
+ erpnext.utils.copy_value_in_all_rows(frm.doc, frm.doc.doctype, frm.doc.name, "time_logs", "project");
+ }
+}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json
index c29c11b746..4c2edf4f03 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.json
+++ b/erpnext/projects/doctype/timesheet/timesheet.json
@@ -1,1133 +1,352 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 0,
- "autoname": "naming_series:",
- "beta": 0,
- "creation": "2013-02-28 17:57:33",
- "custom": 0,
- "description": "",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "naming_series:",
+ "creation": "2013-02-28 17:57:33",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "title",
+ "naming_series",
+ "company",
+ "sales_invoice",
+ "column_break_3",
+ "salary_slip",
+ "status",
+ "project",
+ "employee_detail",
+ "employee",
+ "employee_name",
+ "department",
+ "column_break_9",
+ "user",
+ "start_date",
+ "end_date",
+ "section_break_5",
+ "time_logs",
+ "working_hours",
+ "total_hours",
+ "billing_details",
+ "total_billable_hours",
+ "total_billed_hours",
+ "total_costing_amount",
+ "column_break_10",
+ "total_billable_amount",
+ "total_billed_amount",
+ "per_billed",
+ "section_break_18",
+ "note",
+ "amended_from"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "{employee_name}",
- "fieldname": "title",
- "fieldtype": "Data",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Title",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "allow_on_submit": 1,
+ "default": "{employee_name}",
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Title",
+ "no_copy": 1,
+ "print_hide": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Series",
- "length": 0,
- "no_copy": 0,
- "options": "TS-.YYYY.-",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 1,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Series",
+ "options": "TS-.YYYY.-",
+ "reqd": 1,
+ "set_only_once": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Company",
- "length": 0,
- "no_copy": 0,
- "options": "Company",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "remember_last_selected_value": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "description": "",
- "fieldname": "sales_invoice",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Sales Invoice",
- "length": 0,
- "no_copy": 1,
- "options": "Sales Invoice",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "sales_invoice",
+ "fieldtype": "Link",
+ "label": "Sales Invoice",
+ "no_copy": 1,
+ "options": "Sales Invoice",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "salary_slip",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Salary Slip",
- "length": 0,
- "no_copy": 1,
- "options": "Salary Slip",
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "salary_slip",
+ "fieldtype": "Link",
+ "label": "Salary Slip",
+ "no_copy": 1,
+ "options": "Salary Slip",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Draft",
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 1,
- "label": "Status",
- "length": 0,
- "no_copy": 1,
- "options": "Draft\nSubmitted\nBilled\nPayslip\nCompleted\nCancelled",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "Draft",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_standard_filter": 1,
+ "label": "Status",
+ "no_copy": 1,
+ "options": "Draft\nSubmitted\nBilled\nPayslip\nCompleted\nCancelled",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": "",
- "columns": 0,
- "depends_on": "eval:!doc.work_order || doc.docstatus == 1",
- "fieldname": "employee_detail",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Employee Detail",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "eval:!doc.work_order || doc.docstatus == 1",
+ "fieldname": "employee_detail",
+ "fieldtype": "Section Break",
+ "label": "Employee Detail"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "description": "",
- "fieldname": "employee",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 1,
- "label": "Employee",
- "length": 0,
- "no_copy": 0,
- "options": "Employee",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "in_standard_filter": 1,
+ "label": "Employee",
+ "options": "Employee"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "employee",
- "fieldname": "employee_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 1,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Employee Name",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "employee",
+ "fieldname": "employee_name",
+ "fieldtype": "Data",
+ "in_global_search": 1,
+ "label": "Employee Name",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "employee.department",
- "fieldname": "department",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Department",
- "length": 0,
- "no_copy": 0,
- "options": "Department",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "employee.department",
+ "fieldname": "department",
+ "fieldtype": "Link",
+ "label": "Department",
+ "options": "Department",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_9",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "user",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 1,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "User",
- "length": 0,
- "no_copy": 0,
- "options": "User",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "user",
+ "fieldtype": "Link",
+ "in_global_search": 1,
+ "label": "User",
+ "options": "User",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "start_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Start Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "start_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Start Date",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "end_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "End Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "end_date",
+ "fieldtype": "Date",
+ "label": "End Date",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_5",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "section_break_5",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "time_logs",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Time Sheets",
- "length": 0,
- "no_copy": 0,
- "options": "Timesheet Detail",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "time_logs",
+ "fieldtype": "Table",
+ "label": "Time Sheets",
+ "options": "Timesheet Detail",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "working_hours",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "working_hours",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "description": "",
- "fieldname": "total_hours",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Total Working Hours",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "allow_on_submit": 1,
+ "default": "0",
+ "fieldname": "total_hours",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Total Working Hours",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 1,
- "columns": 0,
- "fieldname": "billing_details",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Billing Details",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "collapsible": 1,
+ "fieldname": "billing_details",
+ "fieldtype": "Section Break",
+ "label": "Billing Details",
+ "permlevel": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "total_billable_hours",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Total Billable Hours",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "allow_on_submit": 1,
+ "fieldname": "total_billable_hours",
+ "fieldtype": "Float",
+ "label": "Total Billable Hours",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "total_billed_hours",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Total Billed Hours",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "allow_on_submit": 1,
+ "fieldname": "total_billed_hours",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Total Billed Hours",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "total_costing_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Total Costing Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "allow_on_submit": 1,
+ "fieldname": "total_costing_amount",
+ "fieldtype": "Currency",
+ "label": "Total Costing Amount",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_10",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_10",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "depends_on": "",
- "description": "",
- "fieldname": "total_billable_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Total Billable Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "allow_on_submit": 1,
+ "default": "0",
+ "fieldname": "total_billable_amount",
+ "fieldtype": "Currency",
+ "label": "Total Billable Amount",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "total_billed_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Total Billed Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "allow_on_submit": 1,
+ "fieldname": "total_billed_amount",
+ "fieldtype": "Currency",
+ "label": "Total Billed Amount",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "per_billed",
- "fieldtype": "Percent",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "% Amount Billed",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "allow_on_submit": 1,
+ "fieldname": "per_billed",
+ "fieldtype": "Percent",
+ "label": "% Amount Billed",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_18",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "section_break_18",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "note",
- "fieldtype": "Text Editor",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Note",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "note",
+ "fieldtype": "Text Editor",
+ "label": "Note"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 1,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Amended From",
- "length": 0,
- "no_copy": 1,
- "options": "Timesheet",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Timesheet",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "label": "Project",
+ "options": "Project"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "fa fa-clock-o",
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-03-05 21:54:02.654690",
- "modified_by": "Administrator",
- "module": "Projects",
- "name": "Timesheet",
- "owner": "Administrator",
+ ],
+ "icon": "fa fa-clock-o",
+ "idx": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-10-29 07:50:35.938231",
+ "modified_by": "Administrator",
+ "module": "Projects",
+ "name": "Timesheet",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Projects User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Projects User",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "HR User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Manufacturing User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Manufacturing User",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Employee",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
+ "create": 1,
+ "read": 1,
+ "role": "Employee",
"write": 1
- },
+ },
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 1,
- "role": "Accounts User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 1,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Accounts User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
+ "permlevel": 1,
+ "read": 1,
+ "role": "Accounts User",
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_order": "ASC",
- "title_field": "title",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "ASC",
+ "title_field": "title"
}
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index 6e97d811fc..29f35958e1 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -146,18 +146,18 @@ cur_frm.cscript.account_head = function(doc, cdt, cdn) {
if(!d.charge_type && d.account_head){
frappe.msgprint(__("Please select Charge Type first"));
frappe.model.set_value(cdt, cdn, "account_head", "");
- } else if(d.account_head && d.charge_type!=="Actual") {
+ } else if (d.account_head) {
frappe.call({
type:"GET",
method: "erpnext.controllers.accounts_controller.get_tax_rate",
args: {"account_head":d.account_head},
callback: function(r) {
- frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0);
+ if (d.charge_type!=="Actual") {
+ frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0);
+ }
frappe.model.set_value(cdt, cdn, "description", r.message.account_name);
}
})
- } else if (d.charge_type == 'Actual' && d.account_head) {
- frappe.model.set_value(cdt, cdn, "description", d.account_head.split(' - ')[0]);
}
}
diff --git a/erpnext/public/js/salary_slip_deductions_report_filters.js b/erpnext/public/js/salary_slip_deductions_report_filters.js
index 2b30e65075..1ca36600c3 100644
--- a/erpnext/public/js/salary_slip_deductions_report_filters.js
+++ b/erpnext/public/js/salary_slip_deductions_report_filters.js
@@ -45,7 +45,7 @@ erpnext.salary_slip_deductions_report_filters = {
},
{
fieldname: "branch",
- label: __("Barnch"),
+ label: __("Branch"),
fieldtype: "Link",
options: "Branch",
}
@@ -63,4 +63,4 @@ erpnext.salary_slip_deductions_report_filters = {
}
});
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.js b/erpnext/regional/doctype/datev_settings/datev_settings.js
index 69747b0b89..f04705929f 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.js
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.js
@@ -2,7 +2,7 @@
// For license information, please see license.txt
frappe.ui.form.on('DATEV Settings', {
- // refresh: function(frm) {
-
- // }
+ refresh: function(frm) {
+ frm.add_custom_button('Show Report', () => frappe.set_route('query-report', 'DATEV'), "fa fa-table");
+ }
});
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.json b/erpnext/regional/doctype/datev_settings/datev_settings.json
index 39486dfc12..713e8e34ef 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.json
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "field:client",
"creation": "2019-08-13 23:56:34.259906",
"doctype": "DocType",
@@ -6,6 +7,7 @@
"engine": "InnoDB",
"field_order": [
"client",
+ "account_number_length",
"column_break_2",
"client_number",
"section_break_4",
@@ -28,8 +30,8 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Client ID",
- "reqd": 1,
- "length": 5
+ "length": 5,
+ "reqd": 1
},
{
"fieldname": "consultant",
@@ -43,8 +45,8 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Consultant ID",
- "reqd": 1,
- "length": 7
+ "length": 7,
+ "reqd": 1
},
{
"fieldname": "column_break_2",
@@ -57,9 +59,17 @@
{
"fieldname": "column_break_6",
"fieldtype": "Column Break"
+ },
+ {
+ "default": "4",
+ "fieldname": "account_number_length",
+ "fieldtype": "Int",
+ "label": "Account Number Length",
+ "reqd": 1
}
],
- "modified": "2019-08-14 00:03:26.616460",
+ "links": [],
+ "modified": "2020-11-05 17:52:11.674329",
"modified_by": "Administrator",
"module": "Regional",
"name": "DATEV Settings",
@@ -104,4 +114,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/uae_vat_account/__init__.py b/erpnext/regional/doctype/uae_vat_account/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/regional/doctype/uae_vat_account/uae_vat_account.json b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.json
new file mode 100644
index 0000000000..73a8169207
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.json
@@ -0,0 +1,35 @@
+{
+ "actions": [],
+ "autoname": "account",
+ "creation": "2020-09-28 11:30:45.472053",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "account"
+ ],
+ "fields": [
+ {
+ "allow_in_quick_entry": 1,
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_preview": 1,
+ "label": "Account",
+ "options": "Account"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-09-28 12:02:56.444007",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "UAE VAT Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
new file mode 100644
index 0000000000..80d6b3a5f1
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class UAEVATAccount(Document):
+ pass
diff --git a/erpnext/regional/doctype/uae_vat_settings/__init__.py b/erpnext/regional/doctype/uae_vat_settings/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py b/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py
new file mode 100644
index 0000000000..b88439f9b8
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestUAEVATSettings(unittest.TestCase):
+ pass
diff --git a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js
new file mode 100644
index 0000000000..07a93010b5
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('UAE VAT Settings', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json
new file mode 100644
index 0000000000..ce2c1d4e14
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json
@@ -0,0 +1,55 @@
+{
+ "actions": [],
+ "autoname": "field:company",
+ "creation": "2020-09-25 12:48:51.463265",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company",
+ "uae_vat_accounts"
+ ],
+ "fields": [
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "uae_vat_accounts",
+ "fieldtype": "Table",
+ "label": "UAE VAT Accounts",
+ "options": "UAE VAT Account",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-09-30 20:08:18.764798",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "UAE VAT Settings",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py
new file mode 100644
index 0000000000..20dc604510
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class UAEVATSettings(Document):
+ pass
diff --git a/erpnext/regional/germany/accounts_controller.py b/erpnext/regional/germany/accounts_controller.py
index 193c8e14a3..5b2b31f204 100644
--- a/erpnext/regional/germany/accounts_controller.py
+++ b/erpnext/regional/germany/accounts_controller.py
@@ -15,8 +15,7 @@ REQUIRED_FIELDS = {
},
{
"field_name": "taxes",
- "regulation": "§ 14 Abs. 4 Nr. 8 UStG",
- "condition": "not exempt_from_sales_tax"
+ "regulation": "§ 14 Abs. 4 Nr. 8 UStG"
},
{
"field_name": "customer_address",
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
index aae734f8e2..f138a807bc 100644
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ b/erpnext/regional/germany/utils/datev/datev_csv.py
@@ -104,9 +104,9 @@ def get_header(filters, csv_class):
# L = Tax client number (Mandantennummer)
datev_settings.get('client_number', '00000'),
# M = Start of the fiscal year (Wirtschaftsjahresbeginn)
- frappe.utils.formatdate(frappe.defaults.get_user_default('year_start_date'), 'yyyyMMdd'),
+ frappe.utils.formatdate(filters.get('fiscal_year_start'), 'yyyyMMdd'),
# N = Length of account numbers (Sachkontenlänge)
- datev_settings.get('account_number_length', '4'),
+ str(filters.get('account_number_length', 4)),
# O = Transaction batch start date (YYYYMMDD)
frappe.utils.formatdate(filters.get('from_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
# P = Transaction batch end date (YYYYMMDD)
@@ -155,20 +155,22 @@ def get_header(filters, csv_class):
return header
-def download_csv_files_as_zip(csv_data_list):
+def zip_and_download(zip_filename, csv_files):
"""
Put CSV files in a zip archive and send that to the client.
Params:
- csv_data_list -- list of dicts [{'file_name': 'EXTF_Buchunsstapel.zip', 'csv_data': get_datev_csv()}]
+ zip_filename Name of the zip file
+ csv_files list of dicts [{'file_name': 'my_file.csv', 'csv_data': 'comma,separated,values'}]
"""
zip_buffer = BytesIO()
- datev_zip = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
- for csv_file in csv_data_list:
- datev_zip.writestr(csv_file.get('file_name'), csv_file.get('csv_data'))
- datev_zip.close()
+ zip_file = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
+ for csv_file in csv_files:
+ zip_file.writestr(csv_file.get('file_name'), csv_file.get('csv_data'))
+
+ zip_file.close()
frappe.response['filecontent'] = zip_buffer.getvalue()
- frappe.response['filename'] = 'DATEV.zip'
+ frappe.response['filename'] = zip_filename
frappe.response['type'] = 'binary'
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 6164e066cd..5458001e9d 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -516,6 +516,9 @@ def get_address_details(data, doc, company_address, billing_address):
data.transType = 1
data.actualToStateCode = data.toStateCode
shipping_address = billing_address
+
+ if doc.gst_category == 'SEZ':
+ data.toStateCode = 99
return data
diff --git a/erpnext/regional/report/datev/datev.js b/erpnext/regional/report/datev/datev.js
index 55f12cf373..4124e3df19 100644
--- a/erpnext/regional/report/datev/datev.js
+++ b/erpnext/regional/report/datev/datev.js
@@ -11,14 +11,14 @@ frappe.query_reports["DATEV"] = {
{
"fieldname": "from_date",
"label": __("From Date"),
- "default": frappe.datetime.month_start(),
+ "default": moment().subtract(1, 'month').startOf('month').format(),
"fieldtype": "Date",
"reqd": 1
},
{
"fieldname": "to_date",
"label": __("To Date"),
- "default": frappe.datetime.now_date(),
+ "default": moment().subtract(1, 'month').endOf('month').format(),
"fieldtype": "Date",
"reqd": 1
},
@@ -30,9 +30,23 @@ frappe.query_reports["DATEV"] = {
}
],
onload: function(query_report) {
+ let company = frappe.query_report.get_filter_value('company');
+ frappe.db.exists('DATEV Settings', company).then((settings_exist) => {
+ if (!settings_exist) {
+ frappe.confirm(__('DATEV Settings for your Company are missing. Would you like to create them now?'),
+ () => frappe.new_doc('DATEV Settings', {'company': company})
+ );
+ }
+ });
+
query_report.page.add_menu_item(__("Download DATEV File"), () => {
const filters = JSON.stringify(query_report.get_values());
window.open(`/api/method/erpnext.regional.report.datev.datev.download_datev_csv?filters=${filters}`);
});
+
+ query_report.page.add_menu_item(__("Change DATEV Settings"), () => {
+ let company = frappe.query_report.get_filter_value('company'); // read company from filters again – it might have changed by now.
+ frappe.set_route('Form', 'DATEV Settings', company);
+ });
}
};
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
index dd818e6054..1e39c57786 100644
--- a/erpnext/regional/report/datev/datev.py
+++ b/erpnext/regional/report/datev/datev.py
@@ -11,9 +11,11 @@ from __future__ import unicode_literals
import json
import frappe
-from frappe import _
from six import string_types
-from erpnext.regional.germany.utils.datev.datev_csv import download_csv_files_as_zip, get_datev_csv
+
+from frappe import _
+from erpnext.accounts.utils import get_fiscal_year
+from erpnext.regional.germany.utils.datev.datev_csv import zip_and_download, get_datev_csv
from erpnext.regional.germany.utils.datev.datev_constants import Transactions, DebtorsCreditors, AccountNames
COLUMNS = [
@@ -92,25 +94,44 @@ COLUMNS = [
def execute(filters=None):
"""Entry point for frappe."""
- validate(filters)
- return COLUMNS, get_transactions(filters, as_dict=0)
+ data = []
+ if filters and validate(filters):
+ data = get_transactions(filters, as_dict=0)
+
+ return COLUMNS, data
def validate(filters):
"""Make sure all mandatory filters and settings are present."""
- if not filters.get('company'):
+ company = filters.get('company')
+ if not company:
frappe.throw(_('Company is a mandatory filter.'))
- if not filters.get('from_date'):
+ from_date = filters.get('from_date')
+ if not from_date:
frappe.throw(_('From Date is a mandatory filter.'))
- if not filters.get('to_date'):
+ to_date = filters.get('to_date')
+ if not to_date:
frappe.throw(_('To Date is a mandatory filter.'))
- try:
- frappe.get_doc('DATEV Settings', filters.get('company'))
- except frappe.DoesNotExistError:
- frappe.throw(_('Please create DATEV Settings for Company {}.').format(filters.get('company')))
+ validate_fiscal_year(from_date, to_date, company)
+
+ if not frappe.db.exists('DATEV Settings', filters.get('company')):
+ frappe.log_error(_('Please create {} for Company {}.').format(
+ '{}'.format(_('DATEV Settings')),
+ frappe.bold(filters.get('company'))
+ ))
+ return False
+
+ return True
+
+
+def validate_fiscal_year(from_date, to_date, company):
+ from_fiscal_year = get_fiscal_year(date=from_date, company=company)
+ to_fiscal_year = get_fiscal_year(date=to_date, company=company)
+ if from_fiscal_year != to_fiscal_year:
+ frappe.throw(_('Dates {} and {} are not in the same fiscal year.').format(from_date, to_date))
def get_transactions(filters, as_dict=1):
@@ -317,17 +338,24 @@ def download_datev_csv(filters):
filters = json.loads(filters)
validate(filters)
+ company = filters.get('company')
+
+ fiscal_year = get_fiscal_year(date=filters.get('from_date'), company=company)
+ filters['fiscal_year_start'] = fiscal_year[1]
# set chart of accounts used
- coa = frappe.get_value('Company', filters.get('company'), 'chart_of_accounts')
+ coa = frappe.get_value('Company', company, 'chart_of_accounts')
filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
+ filters['account_number_length'] = frappe.get_value('DATEV Settings', company, 'account_number_length')
+
transactions = get_transactions(filters)
account_names = get_account_names(filters)
customers = get_customers(filters)
suppliers = get_suppliers(filters)
- download_csv_files_as_zip([
+ zip_name = '{} DATEV.zip'.format(frappe.utils.datetime.date.today())
+ zip_and_download(zip_name, [
{
'file_name': 'EXTF_Buchungsstapel.csv',
'csv_data': get_datev_csv(transactions, filters, csv_class=Transactions)
diff --git a/erpnext/regional/report/uae_vat_201/__init__.py b/erpnext/regional/report/uae_vat_201/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
new file mode 100644
index 0000000000..daa69768c5
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
@@ -0,0 +1,239 @@
+# coding=utf-8
+from __future__ import unicode_literals
+
+import erpnext
+import frappe
+from unittest import TestCase
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse_account
+from erpnext.regional.report.uae_vat_201.uae_vat_201 import (
+ get_total_emiratewise,
+ get_tourist_tax_return_total,
+ get_tourist_tax_return_tax,
+ get_zero_rated_total,
+ get_exempt_total,
+ get_standard_rated_expenses_total,
+ get_standard_rated_expenses_tax,
+)
+
+test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
+
+class TestUaeVat201(TestCase):
+ def setUp(self):
+ frappe.set_user("Administrator")
+
+ frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company UAE VAT'")
+ frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company UAE VAT'")
+
+ make_company("_Test Company UAE VAT", "_TCUV")
+ set_vat_accounts()
+
+ make_customer()
+
+ make_supplier()
+
+ create_warehouse("_Test UAE VAT Supplier Warehouse", company="_Test Company UAE VAT")
+
+ make_item("_Test UAE VAT Item", properties = {"is_zero_rated": 0, "is_exempt": 0})
+ make_item("_Test UAE VAT Zero Rated Item", properties = {"is_zero_rated": 1, "is_exempt": 0})
+ make_item("_Test UAE VAT Exempt Item", properties = {"is_zero_rated": 0, "is_exempt": 1})
+
+ make_sales_invoices()
+
+ create_purchase_invoices()
+
+ def test_uae_vat_201_report(self):
+ filters = {"company": "_Test Company UAE VAT"}
+ total_emiratewise = get_total_emiratewise(filters)
+ amounts_by_emirate = {}
+ for data in total_emiratewise:
+ emirate, amount, vat = data
+ amounts_by_emirate[emirate] = {
+ "raw_amount": amount,
+ "raw_vat_amount": vat,
+ }
+ self.assertEqual(amounts_by_emirate["Sharjah"]["raw_amount"],100)
+ self.assertEqual(amounts_by_emirate["Sharjah"]["raw_vat_amount"],5)
+ self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"],200)
+ self.assertEqual(amounts_by_emirate["Dubai"]["raw_vat_amount"],10)
+ self.assertEqual(get_tourist_tax_return_total(filters),100)
+ self.assertEqual(get_tourist_tax_return_tax(filters),2)
+ self.assertEqual(get_zero_rated_total(filters),100)
+ self.assertEqual(get_exempt_total(filters),100)
+ self.assertEqual(get_standard_rated_expenses_total(filters),250)
+ self.assertEqual(get_standard_rated_expenses_tax(filters),1)
+
+def make_company(company_name, abbr):
+ if not frappe.db.exists("Company", company_name):
+ company = frappe.get_doc({
+ "doctype": "Company",
+ "company_name": company_name,
+ "abbr": abbr,
+ "default_currency": "AED",
+ "country": "United Arab Emirates",
+ "create_chart_of_accounts_based_on": "Standard Template",
+ })
+ company.insert()
+ else:
+ company = frappe.get_doc("Company", company_name)
+
+ company.create_default_warehouses()
+
+ if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": company.name}):
+ company.create_default_cost_center()
+
+ company.save()
+ return company
+
+def set_vat_accounts():
+ if not frappe.db.exists("UAE VAT Settings", "_Test Company UAE VAT"):
+ vat_accounts = frappe.get_all(
+ "Account",
+ fields=["name"],
+ filters = {
+ "company": "_Test Company UAE VAT",
+ "is_group": 0,
+ "account_type": "Tax"
+ }
+ )
+
+ uae_vat_accounts = []
+ for account in vat_accounts:
+ uae_vat_accounts.append({
+ "doctype": "UAE VAT Account",
+ "account": account.name
+ })
+
+ frappe.get_doc({
+ "company": "_Test Company UAE VAT",
+ "uae_vat_accounts": uae_vat_accounts,
+ "doctype": "UAE VAT Settings",
+ }).insert()
+
+def make_customer():
+ if not frappe.db.exists("Customer", "_Test UAE Customer"):
+ customer = frappe.get_doc({
+ "doctype": "Customer",
+ "customer_name": "_Test UAE Customer",
+ "customer_type": "Company",
+ })
+ customer.insert()
+ else:
+ customer = frappe.get_doc("Customer", "_Test UAE Customer")
+
+def make_supplier():
+ if not frappe.db.exists("Supplier", "_Test UAE Supplier"):
+ frappe.get_doc({
+ "supplier_group": "Local",
+ "supplier_name": "_Test UAE Supplier",
+ "supplier_type": "Individual",
+ "doctype": "Supplier",
+ }).insert()
+
+def create_warehouse(warehouse_name, properties=None, company=None):
+ if not company:
+ company = "_Test Company"
+
+ warehouse_id = erpnext.encode_company_abbr(warehouse_name, company)
+ if not frappe.db.exists("Warehouse", warehouse_id):
+ warehouse = frappe.new_doc("Warehouse")
+ warehouse.warehouse_name = warehouse_name
+ warehouse.parent_warehouse = "All Warehouses - _TCUV"
+ warehouse.company = company
+ warehouse.account = get_warehouse_account(warehouse_name, company)
+ if properties:
+ warehouse.update(properties)
+ warehouse.save()
+ return warehouse.name
+ else:
+ return warehouse_id
+
+def make_item(item_code, properties=None):
+ if frappe.db.exists("Item", item_code):
+ return frappe.get_doc("Item", item_code)
+
+ item = frappe.get_doc({
+ "doctype": "Item",
+ "item_code": item_code,
+ "item_name": item_code,
+ "description": item_code,
+ "item_group": "Products"
+ })
+
+ if properties:
+ item.update(properties)
+
+ item.insert()
+
+ return item
+
+def make_sales_invoices():
+ def make_sales_invoices_wrapper(emirate, item, tax = True, tourist_tax= False):
+ si = create_sales_invoice(
+ company="_Test Company UAE VAT",
+ customer = '_Test UAE Customer',
+ currency = 'AED',
+ warehouse = 'Finished Goods - _TCUV',
+ debit_to = 'Debtors - _TCUV',
+ income_account = 'Sales - _TCUV',
+ expense_account = 'Cost of Goods Sold - _TCUV',
+ cost_center = 'Main - _TCUV',
+ item = item,
+ do_not_save=1
+ )
+ si.vat_emirate = emirate
+ if tax:
+ si.append(
+ "taxes", {
+ "charge_type": "On Net Total",
+ "account_head": "VAT 5% - _TCUV",
+ "cost_center": "Main - _TCUV",
+ "description": "VAT 5% @ 5.0",
+ "rate": 5.0
+ }
+ )
+ if tourist_tax:
+ si.tourist_tax_return = 2
+ si.submit()
+
+ #Define Item Names
+ uae_item = "_Test UAE VAT Item"
+ uae_exempt_item = "_Test UAE VAT Exempt Item"
+ uae_zero_rated_item = "_Test UAE VAT Zero Rated Item"
+
+ #Sales Invoice with standard rated expense in Dubai
+ make_sales_invoices_wrapper('Dubai', uae_item)
+ #Sales Invoice with standard rated expense in Sharjah
+ make_sales_invoices_wrapper('Sharjah', uae_item)
+ #Sales Invoice with Tourist Tax Return
+ make_sales_invoices_wrapper('Dubai', uae_item, True, True)
+ #Sales Invoice with Exempt Item
+ make_sales_invoices_wrapper('Sharjah', uae_exempt_item, False)
+ #Sales Invoice with Zero Rated Item
+ make_sales_invoices_wrapper('Sharjah', uae_zero_rated_item, False)
+
+def create_purchase_invoices():
+ pi = make_purchase_invoice(
+ company="_Test Company UAE VAT",
+ supplier = '_Test UAE Supplier',
+ supplier_warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV',
+ warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV',
+ currency = 'AED',
+ cost_center = 'Main - _TCUV',
+ expense_account = 'Cost of Goods Sold - _TCUV',
+ item = "_Test UAE VAT Item",
+ do_not_save=1,
+ uom = "Nos"
+ )
+ pi.append("taxes", {
+ "charge_type": "On Net Total",
+ "account_head": "VAT 5% - _TCUV",
+ "cost_center": "Main - _TCUV",
+ "description": "VAT 5% @ 5.0",
+ "rate": 5.0
+ })
+
+ pi.recoverable_standard_rated_expenses = 1
+
+ pi.submit()
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.html b/erpnext/regional/report/uae_vat_201/uae_vat_201.html
new file mode 100644
index 0000000000..d9b9968d90
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.html
@@ -0,0 +1,77 @@
+{%
+ var report_columns = report.get_columns_for_print();
+ report_columns = report_columns.filter(col => !col.hidden);
+%}
+
+
+
{%= __(report.report_name) %}
+
+{%= __("VAT on Sales and All Other Outputs") %}
+
+
+
+
+ {%= report_columns[0].label %} |
+ {%= report_columns[1].label %} |
+
+ {% for (let i=2; i{%= report_columns[i].label %}
+ {% } %}
+
+
+
+ {% for (let j=1; j<12; j++) { %}
+ {%
+ var row = data[j];
+ %}
+
+ {% for (let i=0; i
+ {% const fieldname = report_columns[i].fieldname; %}
+ {% if (!is_null(row[fieldname])) { %}
+ {%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
+ {% } %}
+
+ {% } %}
+
+ {% } %}
+
+
+
+{%= __("VAT on Expenses and All Other Inputs") %}
+
+
+
+ {%= report_columns[0].label %} |
+ {%= report_columns[1].label %} |
+
+ {% for (let i=2; i{%= report_columns[i].label %}
+ {% } %}
+
+
+
+ {% for (let j=14; j
+ {% for (let i=0; i
+ {% const fieldname = report_columns[i].fieldname; %}
+ {% if (!is_null(row[fieldname])) { %}
+ {%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
+ {% } %}
+
+ {% } %}
+
+ {% } %}
+
+
+
\ No newline at end of file
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.js b/erpnext/regional/report/uae_vat_201/uae_vat_201.js
new file mode 100644
index 0000000000..5957424770
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.js
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["UAE VAT 201"] = {
+ "filters": [
+ {
+ "fieldname": "company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "options": "Company",
+ "reqd": 1,
+ "default": frappe.defaults.get_user_default("Company")
+ },
+ {
+ "fieldname": "from_date",
+ "label": __("From Date"),
+ "fieldtype": "Date",
+ "reqd": 1,
+ "default": frappe.datetime.add_months(frappe.datetime.get_today(), -3),
+ },
+ {
+ "fieldname": "to_date",
+ "label": __("To Date"),
+ "fieldtype": "Date",
+ "reqd": 1,
+ "default": frappe.datetime.get_today()
+ },
+ ],
+ "formatter": function(value, row, column, data, default_formatter) {
+ if (data
+ && (data.legend=='VAT on Sales and All Other Outputs' || data.legend=='VAT on Expenses and All Other Inputs')
+ && data.legend==value) {
+ value = $(`${value}`);
+ var $value = $(value).css("font-weight", "bold");
+ value = $value.wrap("").parent().html();
+ }
+ return value;
+ },
+};
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.json b/erpnext/regional/report/uae_vat_201/uae_vat_201.json
new file mode 100644
index 0000000000..8a88bcd3e2
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.json
@@ -0,0 +1,22 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2020-09-10 08:51:02.298482",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-09-10 08:51:02.298482",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "UAE VAT 201",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "GL Entry",
+ "report_name": "UAE VAT 201",
+ "report_type": "Script Report",
+ "roles": []
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py
new file mode 100644
index 0000000000..b0614238ba
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py
@@ -0,0 +1,339 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+
+def execute(filters=None):
+ columns = get_columns()
+ data, emirates, amounts_by_emirate = get_data(filters)
+ return columns, data
+
+def get_columns():
+ """Creates a list of dictionaries that are used to generate column headers of the data table."""
+ return [
+ {
+ "fieldname": "no",
+ "label": _("No"),
+ "fieldtype": "Data",
+ "width": 50
+ },
+ {
+ "fieldname": "legend",
+ "label": _("Legend"),
+ "fieldtype": "Data",
+ "width": 300
+ },
+ {
+ "fieldname": "amount",
+ "label": _("Amount (AED)"),
+ "fieldtype": "Currency",
+ "width": 125,
+ },
+ {
+ "fieldname": "vat_amount",
+ "label": _("VAT Amount (AED)"),
+ "fieldtype": "Currency",
+ "width": 150,
+ }
+ ]
+
+def get_data(filters = None):
+ """Returns the list of dictionaries. Each dictionary is a row in the datatable and chart data."""
+ data = []
+ emirates, amounts_by_emirate = append_vat_on_sales(data, filters)
+ append_vat_on_expenses(data, filters)
+ return data, emirates, amounts_by_emirate
+
+def append_vat_on_sales(data, filters):
+ """Appends Sales and All Other Outputs."""
+ append_data(data, '', _('VAT on Sales and All Other Outputs'), '', '')
+
+ emirates, amounts_by_emirate = standard_rated_expenses_emiratewise(data, filters)
+
+ append_data(data, '2',
+ _('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'),
+ frappe.format((-1) * get_tourist_tax_return_total(filters), 'Currency'),
+ frappe.format((-1) * get_tourist_tax_return_tax(filters), 'Currency'))
+
+ append_data(data, '3', _('Supplies subject to the reverse charge provision'),
+ frappe.format(get_reverse_charge_total(filters), 'Currency'),
+ frappe.format(get_reverse_charge_tax(filters), 'Currency'))
+
+ append_data(data, '4', _('Zero Rated'),
+ frappe.format(get_zero_rated_total(filters), 'Currency'), "-")
+
+ append_data(data, '5', _('Exempt Supplies'),
+ frappe.format(get_exempt_total(filters), 'Currency'),"-")
+
+ append_data(data, '', '', '', '')
+
+ return emirates, amounts_by_emirate
+
+def standard_rated_expenses_emiratewise(data, filters):
+ """Append emiratewise standard rated expenses and vat."""
+ total_emiratewise = get_total_emiratewise(filters)
+ emirates = get_emirates()
+ amounts_by_emirate = {}
+ for emirate, amount, vat in total_emiratewise:
+ amounts_by_emirate[emirate] = {
+ "legend": emirate,
+ "raw_amount": amount,
+ "raw_vat_amount": vat,
+ "amount": frappe.format(amount, 'Currency'),
+ "vat_amount": frappe.format(vat, 'Currency'),
+ }
+ amounts_by_emirate = append_emiratewise_expenses(data, emirates, amounts_by_emirate)
+ return emirates, amounts_by_emirate
+
+def append_emiratewise_expenses(data, emirates, amounts_by_emirate):
+ """Append emiratewise standard rated expenses and vat."""
+ for no, emirate in enumerate(emirates, 97):
+ if emirate in amounts_by_emirate:
+ amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(no))
+ amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate)
+ data.append(amounts_by_emirate[emirate])
+ else:
+ append_data(data, _('1{0}').format(chr(no)),
+ _('Standard rated supplies in {0}').format(emirate),
+ frappe.format(0, 'Currency'), frappe.format(0, 'Currency'))
+ return amounts_by_emirate
+
+def append_vat_on_expenses(data, filters):
+ """Appends Expenses and All Other Inputs."""
+ append_data(data, '', _('VAT on Expenses and All Other Inputs'), '', '')
+ append_data(data, '9', _('Standard Rated Expenses'),
+ frappe.format(get_standard_rated_expenses_total(filters), 'Currency'),
+ frappe.format(get_standard_rated_expenses_tax(filters), 'Currency'))
+ append_data(data, '10', _('Supplies subject to the reverse charge provision'),
+ frappe.format(get_reverse_charge_recoverable_total(filters), 'Currency'),
+ frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency'))
+
+def append_data(data, no, legend, amount, vat_amount):
+ """Returns data with appended value."""
+ data.append({"no": no, "legend":legend, "amount": amount, "vat_amount": vat_amount})
+
+def get_total_emiratewise(filters):
+ """Returns Emiratewise Amount and Taxes."""
+ conditions = get_conditions(filters)
+ try:
+ return frappe.db.sql("""
+ select
+ s.vat_emirate as emirate, sum(i.base_amount) as total, sum(s.total_taxes_and_charges)
+ from
+ `tabSales Invoice Item` i inner join `tabSales Invoice` s
+ on
+ i.parent = s.name
+ where
+ s.docstatus = 1 and i.is_exempt != 1 and i.is_zero_rated != 1
+ {where_conditions}
+ group by
+ s.vat_emirate;
+ """.format(where_conditions=conditions), filters)
+ except (IndexError, TypeError):
+ return 0
+
+def get_emirates():
+ """Returns a List of emirates in the order that they are to be displayed."""
+ return [
+ 'Abu Dhabi',
+ 'Dubai',
+ 'Sharjah',
+ 'Ajman',
+ 'Umm Al Quwain',
+ 'Ras Al Khaimah',
+ 'Fujairah'
+ ]
+
+def get_filters(filters):
+ """The conditions to be used to filter data to calculate the total sale."""
+ query_filters = []
+ if filters.get("company"):
+ query_filters.append(["company", '=', filters['company']])
+ if filters.get("from_date"):
+ query_filters.append(["posting_date", '>=', filters['from_date']])
+ if filters.get("from_date"):
+ query_filters.append(["posting_date", '<=', filters['to_date']])
+ return query_filters
+
+def get_reverse_charge_total(filters):
+ """Returns the sum of the total of each Purchase invoice made."""
+ query_filters = get_filters(filters)
+ query_filters.append(['reverse_charge', '=', 'Y'])
+ query_filters.append(['docstatus', '=', 1])
+ try:
+ return frappe.db.get_all('Purchase Invoice',
+ filters = query_filters,
+ fields = ['sum(total)'],
+ as_list=True,
+ limit = 1
+ )[0][0] or 0
+ except (IndexError, TypeError):
+ return 0
+
+def get_reverse_charge_tax(filters):
+ """Returns the sum of the tax of each Purchase invoice made."""
+ conditions = get_conditions_join(filters)
+ return frappe.db.sql("""
+ select sum(debit) from
+ `tabPurchase Invoice` p inner join `tabGL Entry` gl
+ on
+ gl.voucher_no = p.name
+ where
+ p.reverse_charge = "Y"
+ and p.docstatus = 1
+ and gl.docstatus = 1
+ and account in (select account from `tabUAE VAT Account` where parent=%(company)s)
+ {where_conditions} ;
+ """.format(where_conditions=conditions), filters)[0][0] or 0
+
+def get_reverse_charge_recoverable_total(filters):
+ """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge."""
+ query_filters = get_filters(filters)
+ query_filters.append(['reverse_charge', '=', 'Y'])
+ query_filters.append(['recoverable_reverse_charge', '>', '0'])
+ query_filters.append(['docstatus', '=', 1])
+ try:
+ return frappe.db.get_all('Purchase Invoice',
+ filters = query_filters,
+ fields = ['sum(total)'],
+ as_list=True,
+ limit = 1
+ )[0][0] or 0
+ except (IndexError, TypeError):
+ return 0
+
+def get_reverse_charge_recoverable_tax(filters):
+ """Returns the sum of the tax of each Purchase invoice made."""
+ conditions = get_conditions_join(filters)
+ return frappe.db.sql("""
+ select
+ sum(debit * p.recoverable_reverse_charge / 100)
+ from
+ `tabPurchase Invoice` p inner join `tabGL Entry` gl
+ on
+ gl.voucher_no = p.name
+ where
+ p.reverse_charge = "Y"
+ and p.docstatus = 1
+ and p.recoverable_reverse_charge > 0
+ and gl.docstatus = 1
+ and account in (select account from `tabUAE VAT Account` where parent=%(company)s)
+ {where_conditions} ;
+ """.format(where_conditions=conditions), filters)[0][0] or 0
+
+def get_conditions_join(filters):
+ """The conditions to be used to filter data to calculate the total vat."""
+ conditions = ""
+ for opts in (("company", " and p.company=%(company)s"),
+ ("from_date", " and p.posting_date>=%(from_date)s"),
+ ("to_date", " and p.posting_date<=%(to_date)s")):
+ if filters.get(opts[0]):
+ conditions += opts[1]
+ return conditions
+
+def get_standard_rated_expenses_total(filters):
+ """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge."""
+ query_filters = get_filters(filters)
+ query_filters.append(['recoverable_standard_rated_expenses', '>', 0])
+ query_filters.append(['docstatus', '=', 1])
+ try:
+ return frappe.db.get_all('Purchase Invoice',
+ filters = query_filters,
+ fields = ['sum(total)'],
+ as_list=True,
+ limit = 1
+ )[0][0] or 0
+ except (IndexError, TypeError):
+ return 0
+
+def get_standard_rated_expenses_tax(filters):
+ """Returns the sum of the tax of each Purchase invoice made."""
+ query_filters = get_filters(filters)
+ query_filters.append(['recoverable_standard_rated_expenses', '>', 0])
+ query_filters.append(['docstatus', '=', 1])
+ try:
+ return frappe.db.get_all('Purchase Invoice',
+ filters = query_filters,
+ fields = ['sum(recoverable_standard_rated_expenses)'],
+ as_list=True,
+ limit = 1
+ )[0][0] or 0
+ except (IndexError, TypeError):
+ return 0
+
+def get_tourist_tax_return_total(filters):
+ """Returns the sum of the total of each Sales invoice with non zero tourist_tax_return."""
+ query_filters = get_filters(filters)
+ query_filters.append(['tourist_tax_return', '>', 0])
+ query_filters.append(['docstatus', '=', 1])
+ try:
+ return frappe.db.get_all('Sales Invoice',
+ filters = query_filters,
+ fields = ['sum(total)'],
+ as_list=True,
+ limit = 1
+ )[0][0] or 0
+ except (IndexError, TypeError):
+ return 0
+
+def get_tourist_tax_return_tax(filters):
+ """Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return."""
+ query_filters = get_filters(filters)
+ query_filters.append(['tourist_tax_return', '>', 0])
+ query_filters.append(['docstatus', '=', 1])
+ try:
+ return frappe.db.get_all('Sales Invoice',
+ filters = query_filters,
+ fields = ['sum(tourist_tax_return)'],
+ as_list=True,
+ limit = 1
+ )[0][0] or 0
+ except (IndexError, TypeError):
+ return 0
+
+def get_zero_rated_total(filters):
+ """Returns the sum of each Sales Invoice Item Amount which is zero rated."""
+ conditions = get_conditions(filters)
+ try:
+ return frappe.db.sql("""
+ select
+ sum(i.base_amount) as total
+ from
+ `tabSales Invoice Item` i inner join `tabSales Invoice` s
+ on
+ i.parent = s.name
+ where
+ s.docstatus = 1 and i.is_zero_rated = 1
+ {where_conditions} ;
+ """.format(where_conditions=conditions), filters)[0][0] or 0
+ except (IndexError, TypeError):
+ return 0
+
+def get_exempt_total(filters):
+ """Returns the sum of each Sales Invoice Item Amount which is Vat Exempt."""
+ conditions = get_conditions(filters)
+ try:
+ return frappe.db.sql("""
+ select
+ sum(i.base_amount) as total
+ from
+ `tabSales Invoice Item` i inner join `tabSales Invoice` s
+ on
+ i.parent = s.name
+ where
+ s.docstatus = 1 and i.is_exempt = 1
+ {where_conditions} ;
+ """.format(where_conditions=conditions), filters)[0][0] or 0
+ except (IndexError, TypeError):
+ return 0
+def get_conditions(filters):
+ """The conditions to be used to filter data to calculate the total sale."""
+ conditions = ""
+ for opts in (("company", " and company=%(company)s"),
+ ("from_date", " and posting_date>=%(from_date)s"),
+ ("to_date", " and posting_date<=%(to_date)s")):
+ if filters.get(opts[0]):
+ conditions += opts[1]
+ return conditions
diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py
index 250659e54d..013ae5cf73 100644
--- a/erpnext/regional/united_arab_emirates/setup.py
+++ b/erpnext/regional/united_arab_emirates/setup.py
@@ -5,24 +5,30 @@ from __future__ import unicode_literals
import frappe, os, json
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+from frappe.permissions import add_permission, update_permission_property
from erpnext.setup.setup_wizard.operations.taxes_setup import create_sales_tax
def setup(company=None, patch=True):
make_custom_fields()
add_print_formats()
-
+ add_custom_roles_for_reports()
+ add_permissions()
if company:
create_sales_tax(company)
def make_custom_fields():
+ is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated',
+ fieldtype='Check', fetch_from='item_code.is_zero_rated', insert_after='description',
+ print_hide=1)
+ is_exempt = dict(fieldname='is_exempt', label='Is Exempt',
+ fieldtype='Check', fetch_from='item_code.is_exempt', insert_after='is_zero_rated',
+ print_hide=1)
+
invoice_fields = [
dict(fieldname='vat_section', label='VAT Details', fieldtype='Section Break',
insert_after='group_same_items', print_hide=1, collapsible=1),
dict(fieldname='permit_no', label='Permit Number',
fieldtype='Data', insert_after='vat_section', print_hide=1),
- dict(fieldname='reverse_charge_applicable', label='Reverse Charge Applicable',
- fieldtype='Select', insert_after='permit_no', print_hide=1,
- options='Y\nN', default='N')
]
purchase_invoice_fields = [
@@ -31,7 +37,16 @@ def make_custom_fields():
fetch_from='company.tax_id', print_hide=1),
dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
fieldtype='Read Only', insert_after='supplier_name',
- fetch_from='supplier.supplier_name_in_arabic', print_hide=1)
+ fetch_from='supplier.supplier_name_in_arabic', print_hide=1),
+ dict(fieldname='recoverable_standard_rated_expenses', print_hide=1, default='0',
+ label='Recoverable Standard Rated Expenses (AED)', insert_after='permit_no',
+ fieldtype='Currency', ),
+ dict(fieldname='reverse_charge', label='Reverse Charge Applicable',
+ fieldtype='Select', insert_after='recoverable_standard_rated_expenses', print_hide=1,
+ options='Y\nN', default='N'),
+ dict(fieldname='recoverable_reverse_charge', label='Recoverable Reverse Charge (Percentage)',
+ insert_after='reverse_charge', fieldtype='Percent', print_hide=1,
+ depends_on="eval:doc.reverse_charge=='Y'", default='100.000'),
]
sales_invoice_fields = [
@@ -41,6 +56,11 @@ def make_custom_fields():
dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
fieldtype='Read Only', insert_after='customer_name',
fetch_from='customer.customer_name_in_arabic', print_hide=1),
+ dict(fieldname='vat_emirate', label='VAT Emirate', insert_after='permit_no', fieldtype='Select',
+ options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain',
+ fetch_from='company_address.emirate'),
+ dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)',
+ insert_after='vat_emirate', fieldtype='Currency', print_hide=1, default='0'),
]
invoice_item_fields = [
@@ -67,6 +87,12 @@ def make_custom_fields():
'Item': [
dict(fieldname='tax_code', label='Tax Code',
fieldtype='Data', insert_after='item_group'),
+ dict(fieldname='is_zero_rated', label='Is Zero Rated',
+ fieldtype='Check', insert_after='tax_code',
+ print_hide=1),
+ dict(fieldname='is_exempt', label='Is Exempt',
+ fieldtype='Check', insert_after='is_zero_rated',
+ print_hide=1)
],
'Customer': [
dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
@@ -76,13 +102,17 @@ def make_custom_fields():
dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
fieldtype='Data', insert_after='supplier_name'),
],
+ 'Address': [
+ dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state',
+ options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain')
+ ],
'Purchase Invoice': purchase_invoice_fields + invoice_fields,
'Purchase Order': purchase_invoice_fields + invoice_fields,
'Purchase Receipt': purchase_invoice_fields + invoice_fields,
'Sales Invoice': sales_invoice_fields + invoice_fields,
'Sales Order': sales_invoice_fields + invoice_fields,
'Delivery Note': sales_invoice_fields + invoice_fields,
- 'Sales Invoice Item': invoice_item_fields + delivery_date_field,
+ 'Sales Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
'Purchase Invoice Item': invoice_item_fields,
'Sales Order Item': invoice_item_fields,
'Delivery Note Item': invoice_item_fields,
@@ -101,3 +131,25 @@ def add_print_formats():
frappe.db.sql(""" update `tabPrint Format` set disabled = 0 where
name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """)
+
+def add_custom_roles_for_reports():
+ """Add Access Control to UAE VAT 201."""
+ if not frappe.db.get_value('Custom Role', dict(report='UAE VAT 201')):
+ frappe.get_doc(dict(
+ doctype='Custom Role',
+ report='UAE VAT 201',
+ roles= [
+ dict(role='Accounts User'),
+ dict(role='Accounts Manager'),
+ dict(role='Auditor')
+ ]
+ )).insert()
+
+def add_permissions():
+ """Add Permissions for UAE VAT Settings and UAE VAT Account."""
+ for doctype in ('UAE VAT Settings', 'UAE VAT Account'):
+ add_permission(doctype, 'All', 0)
+ for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
+ add_permission(doctype, role, 0)
+ update_permission_property(doctype, role, 0, 'write', 1)
+ update_permission_property(doctype, role, 0, 'create', 1)
diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py
index a0425f6b1c..7d5fd6ecf8 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import flt
+from frappe import _
+import erpnext
+from frappe.utils import flt, round_based_on_smallest_currency_fraction, money_in_words
from erpnext.controllers.taxes_and_totals import get_itemised_tax
from six import iteritems
@@ -26,4 +28,134 @@ def update_itemised_tax_data(doc):
row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
- row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
\ No newline at end of file
+ row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
+
+def get_account_currency(account):
+ """Helper function to get account currency."""
+ if not account:
+ return
+ def generator():
+ account_currency, company = frappe.get_cached_value(
+ "Account",
+ account,
+ ["account_currency",
+ "company"]
+ )
+ if not account_currency:
+ account_currency = frappe.get_cached_value('Company', company, "default_currency")
+
+ return account_currency
+
+ return frappe.local_cache("account_currency", account, generator)
+
+def get_tax_accounts(company):
+ """Get the list of tax accounts for a specific company."""
+ tax_accounts_dict = frappe._dict()
+ tax_accounts_list = frappe.get_all("UAE VAT Account",
+ filters={"parent": company},
+ fields=["Account"]
+ )
+
+ if not tax_accounts_list and not frappe.flags.in_test:
+ frappe.throw(_('Please set Vat Accounts for Company: "{0}" in UAE VAT Settings').format(company))
+ for tax_account in tax_accounts_list:
+ for account, name in tax_account.items():
+ tax_accounts_dict[name] = name
+
+ return tax_accounts_dict
+
+def update_grand_total_for_rcm(doc, method):
+ """If the Reverse Charge is Applicable subtract the tax amount from the grand total and update in the form."""
+ country = frappe.get_cached_value('Company', doc.company, 'country')
+
+ if country != 'United Arab Emirates':
+ return
+
+ if not doc.total_taxes_and_charges:
+ return
+
+ if doc.reverse_charge == 'Y':
+ tax_accounts = get_tax_accounts(doc.company)
+
+ base_vat_tax = 0
+ vat_tax = 0
+
+ for tax in doc.get('taxes'):
+ if tax.category not in ("Total", "Valuation and Total"):
+ continue
+
+ if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts:
+ base_vat_tax += tax.base_tax_amount_after_discount_amount
+ vat_tax += tax.tax_amount_after_discount_amount
+
+ doc.taxes_and_charges_added -= vat_tax
+ doc.total_taxes_and_charges -= vat_tax
+ doc.base_taxes_and_charges_added -= base_vat_tax
+ doc.base_total_taxes_and_charges -= base_vat_tax
+
+ update_totals(vat_tax, base_vat_tax, doc)
+
+def update_totals(vat_tax, base_vat_tax, doc):
+ """Update the grand total values in the form."""
+ doc.base_grand_total -= base_vat_tax
+ doc.grand_total -= vat_tax
+
+ if doc.meta.get_field("rounded_total"):
+
+ if doc.is_rounded_total_disabled():
+ doc.outstanding_amount = doc.grand_total
+
+ else:
+ doc.rounded_total = round_based_on_smallest_currency_fraction(doc.grand_total,
+ doc.currency, doc.precision("rounded_total"))
+ doc.rounding_adjustment = flt(doc.rounded_total - doc.grand_total,
+ doc.precision("rounding_adjustment"))
+ doc.outstanding_amount = doc.rounded_total or doc.grand_total
+
+ doc.in_words = money_in_words(doc.grand_total, doc.currency)
+ doc.base_in_words = money_in_words(doc.base_grand_total, erpnext.get_company_currency(doc.company))
+ doc.set_payment_schedule()
+
+def make_regional_gl_entries(gl_entries, doc):
+ """Hooked to make_regional_gl_entries in Purchase Invoice.It appends the region specific general ledger entries to the list of GL Entries."""
+ country = frappe.get_cached_value('Company', doc.company, 'country')
+
+ if country != 'United Arab Emirates':
+ return gl_entries
+
+ if doc.reverse_charge == 'Y':
+ tax_accounts = get_tax_accounts(doc.company)
+ for tax in doc.get('taxes'):
+ if tax.category not in ("Total", "Valuation and Total"):
+ continue
+ gl_entries = make_gl_entry(tax, gl_entries, doc, tax_accounts)
+ return gl_entries
+
+def make_gl_entry(tax, gl_entries, doc, tax_accounts):
+ dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
+ if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts:
+ account_currency = get_account_currency(tax.account_head)
+
+ gl_entries.append(doc.get_gl_dict({
+ "account": tax.account_head,
+ "cost_center": tax.cost_center,
+ "posting_date": doc.posting_date,
+ "against": doc.supplier,
+ dr_or_cr: tax.base_tax_amount_after_discount_amount,
+ dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \
+ if account_currency==doc.company_currency \
+ else tax.tax_amount_after_discount_amount
+ }, account_currency, item=tax
+ ))
+ return gl_entries
+
+
+def validate_returns(doc, method):
+ """Standard Rated expenses should not be set when Reverse Charge Applicable is set."""
+ country = frappe.get_cached_value('Company', doc.company, 'country')
+ if country != 'United Arab Emirates':
+ return
+ if doc.reverse_charge == 'Y' and flt(doc.recoverable_standard_rated_expenses) != 0:
+ frappe.throw(_(
+ "Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y"
+ ))
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index 5b85187ccb..3eba62bc19 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-24 19:29:08",
@@ -932,7 +933,7 @@
"is_submittable": 1,
"links": [],
"max_attachments": 1,
- "modified": "2020-07-26 17:46:19.951223",
+ "modified": "2020-10-30 13:58:59.212060",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 77c1787c26..3d64ac3780 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-06-18 12:39:59",
@@ -1460,7 +1461,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-07 14:30:01.782617",
+ "modified": "2020-10-30 13:59:18.628077",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index ec1c82339b..04d85e575c 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -844,7 +844,8 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t
"contact_email",
"contact_person",
"taxes_and_charges",
- "shipping_address"
+ "shipping_address",
+ "terms"
],
"validation": {
"docstatus": ["=", 1]
@@ -863,7 +864,10 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t
"field_no_map": [
"rate",
"price_list_rate",
- "item_tax_template"
+ "item_tax_template",
+ "discount_percentage",
+ "discount_amount",
+ "pricing_rules"
],
"postprocess": update_item,
"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map
@@ -917,7 +921,8 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
"contact_email",
"contact_person",
"taxes_and_charges",
- "shipping_address"
+ "shipping_address",
+ "terms"
],
"validation": {
"docstatus": ["=", 1]
@@ -937,7 +942,10 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
"rate",
"price_list_rate",
"item_tax_template",
- "supplier"
+ "discount_percentage",
+ "discount_amount",
+ "supplier",
+ "pricing_rules"
],
"postprocess": update_item,
"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.item_code in items_to_map
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index a33d401b57..643e7cf38b 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1064,6 +1064,7 @@ def make_sales_order(**args):
so.company = args.company or "_Test Company"
so.customer = args.customer or "_Test Customer"
so.currency = args.currency or "INR"
+ so.po_no = args.po_no or '12345'
if args.selling_price_list:
so.selling_price_list = args.selling_price_list
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index a690050f79..062cba19e6 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -62,6 +62,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va
`tabItem` item {bin_join_selection}
WHERE
item.disabled = 0
+ AND item.is_stock_item = 1
AND item.has_variants = 0
AND item.is_sales_item = 1
AND item.is_fixed_asset = 0
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index f882db60c5..cbf67b4cd6 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -90,29 +90,41 @@ frappe.ui.form.on("Company", {
frm.toggle_enable("default_currency", (frm.doc.__onload &&
!frm.doc.__onload.transactions_exist));
- frm.add_custom_button(__('Create Tax Template'), function() {
- frm.trigger("make_default_tax_template");
- });
+ if (frm.has_perm('write')) {
+ frm.add_custom_button(__('Create Tax Template'), function() {
+ frm.trigger("make_default_tax_template");
+ });
+ }
- frm.add_custom_button(__('Cost Centers'), function() {
- frappe.set_route('Tree', 'Cost Center', {'company': frm.doc.name})
- }, __("View"));
+ if (frappe.perm.has_perm("Cost Center", 0, 'read')) {
+ frm.add_custom_button(__('Cost Centers'), function() {
+ frappe.set_route('Tree', 'Cost Center', {'company': frm.doc.name});
+ }, __("View"));
+ }
- frm.add_custom_button(__('Chart of Accounts'), function() {
- frappe.set_route('Tree', 'Account', {'company': frm.doc.name})
- }, __("View"));
+ if (frappe.perm.has_perm("Account", 0, 'read')) {
+ frm.add_custom_button(__('Chart of Accounts'), function() {
+ frappe.set_route('Tree', 'Account', {'company': frm.doc.name});
+ }, __("View"));
+ }
- frm.add_custom_button(__('Sales Tax Template'), function() {
- frappe.set_route('List', 'Sales Taxes and Charges Template', {'company': frm.doc.name});
- }, __("View"));
+ if (frappe.perm.has_perm("Sales Taxes and Charges Template", 0, 'read')) {
+ frm.add_custom_button(__('Sales Tax Template'), function() {
+ frappe.set_route('List', 'Sales Taxes and Charges Template', {'company': frm.doc.name});
+ }, __("View"));
+ }
- frm.add_custom_button(__('Purchase Tax Template'), function() {
- frappe.set_route('List', 'Purchase Taxes and Charges Template', {'company': frm.doc.name});
- }, __("View"));
+ if (frappe.perm.has_perm("Purchase Taxes and Charges Template", 0, 'read')) {
+ frm.add_custom_button(__('Purchase Tax Template'), function() {
+ frappe.set_route('List', 'Purchase Taxes and Charges Template', {'company': frm.doc.name});
+ }, __("View"));
+ }
- frm.add_custom_button(__('Default Tax Template'), function() {
- frm.trigger("make_default_tax_template");
- }, __('Create'));
+ if (frm.has_perm('write')) {
+ frm.add_custom_button(__('Default Tax Template'), function() {
+ frm.trigger("make_default_tax_template");
+ }, __('Create'));
+ }
}
erpnext.company.set_chart_of_accounts_options(frm.doc);
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index c94831ef93..566f20cfa1 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -27,7 +27,8 @@ def delete_company_transactions(company_name):
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
"Party Account", "Employee", "Sales Taxes and Charges Template",
"Purchase Taxes and Charges Template", "POS Profile", "BOM",
- "Company", "Bank Account"):
+ "Company", "Bank Account", "Item Tax Template", "Mode Of Payment",
+ "Item Default"):
delete_for_doctype(doctype, company_name)
# reset company values
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index b30bd7814b..cbb4c7c5de 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -48,12 +48,8 @@ class EmailDigest(Document):
recipients = list(filter(lambda r: r in valid_users,
self.recipient_list.split("\n")))
- original_user = frappe.session.user
-
if recipients:
for user_id in recipients:
- frappe.set_user(user_id)
- frappe.set_user_lang(user_id)
msg_for_this_recipient = self.get_msg_html()
if msg_for_this_recipient:
frappe.sendmail(
@@ -64,9 +60,6 @@ class EmailDigest(Document):
reference_name = self.name,
unsubscribe_message = _("Unsubscribe from this Email Digest"))
- frappe.set_user(original_user)
- frappe.set_user_lang(original_user)
-
def get_msg_html(self):
"""Build email digest content"""
frappe.flags.ignore_account_permission = True
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 32fe760e05..111e3940b3 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-24 19:29:09",
@@ -414,7 +415,8 @@
{
"fieldname": "company_address_display",
"fieldtype": "Small Text",
- "label": "Company Address"
+ "label": "Company Address",
+ "read_only": 1
},
{
"collapsible": 1,
@@ -1266,7 +1268,7 @@
"idx": 146,
"is_submittable": 1,
"links": [],
- "modified": "2020-09-08 11:22:09.056684",
+ "modified": "2020-11-19 11:22:09.056684",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index fa07a2510c..6b4663a688 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -509,9 +509,15 @@ class TestDeliveryNote(unittest.TestCase):
self.assertEqual(dn.status, "To Bill")
self.assertEqual(dn.per_billed, 0)
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(dn.po_no, so.po_no)
+
si = make_sales_invoice(dn.name)
si.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(dn.po_no, si.po_no)
+
dn.load_from_db()
self.assertEqual(dn.get("items")[0].billed_amt, 200)
self.assertEqual(dn.per_billed, 100)
@@ -528,16 +534,25 @@ class TestDeliveryNote(unittest.TestCase):
si.insert()
si.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(so.po_no, si.po_no)
+
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
dn1 = make_delivery_note(so.name)
dn1.get("items")[0].qty = 2
dn1.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(so.po_no, dn1.po_no)
+
dn2 = make_delivery_note(so.name)
dn2.get("items")[0].qty = 3
dn2.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(so.po_no, dn2.po_no)
+
dn1.load_from_db()
self.assertEqual(dn1.get("items")[0].billed_amt, 200)
self.assertEqual(dn1.per_billed, 100)
@@ -559,9 +574,15 @@ class TestDeliveryNote(unittest.TestCase):
dn1.get("items")[0].qty = 2
dn1.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(dn1.po_no, so.po_no)
+
si1 = make_sales_invoice(dn1.name)
si1.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(dn1.po_no, si1.po_no)
+
dn1.load_from_db()
self.assertEqual(dn1.per_billed, 100)
@@ -569,10 +590,16 @@ class TestDeliveryNote(unittest.TestCase):
si2.get("items")[0].qty = 4
si2.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(si2.po_no, so.po_no)
+
dn2 = make_delivery_note(so.name)
dn2.get("items")[0].qty = 5
dn2.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(dn2.po_no, so.po_no)
+
dn1.load_from_db()
self.assertEqual(dn1.get("items")[0].billed_amt, 200)
self.assertEqual(dn1.per_billed, 100)
@@ -592,9 +619,15 @@ class TestDeliveryNote(unittest.TestCase):
si = make_sales_invoice(so.name)
si.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(so.po_no, si.po_no)
+
dn = make_delivery_note(si.name)
dn.submit()
+ # Testing if Customer's Purchase Order No was rightly copied
+ self.assertEqual(dn.po_no, si.po_no)
+
self.assertEqual(dn.get("items")[0].billed_amt, 1000)
self.assertEqual(dn.per_billed, 100)
self.assertEqual(dn.status, "Completed")
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index 51b47c50a3..bed5ea9ab6 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -4,14 +4,13 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-
-
-class ItemPriceDuplicateItem(frappe.ValidationError): pass
-
-
from frappe.model.document import Document
+class ItemPriceDuplicateItem(frappe.ValidationError):
+ pass
+
+
class ItemPrice(Document):
def validate(self):
@@ -23,7 +22,7 @@ class ItemPrice(Document):
def validate_item(self):
if not frappe.db.exists("Item", self.item_code):
- frappe.throw(_("Item {0} not found").format(self.item_code))
+ frappe.throw(_("Item {0} not found.").format(self.item_code))
def validate_dates(self):
if self.valid_from and self.valid_upto:
@@ -38,40 +37,45 @@ class ItemPrice(Document):
if not price_list_details:
link = frappe.utils.get_link_to_form('Price List', self.price_list)
- frappe.throw("The price list {0} does not exists or disabled".
- format(link))
+ frappe.throw("The price list {0} does not exist or is disabled".format(link))
self.buying, self.selling, self.currency = price_list_details
def update_item_details(self):
if self.item_code:
- self.item_name, self.item_description = frappe.db.get_value("Item",
- self.item_code,["item_name", "description"])
+ self.item_name, self.item_description = frappe.db.get_value("Item", self.item_code,["item_name", "description"])
def check_duplicates(self):
- conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s"
- condition_data_dict = dict(item_code=self.item_code, price_list=self.price_list, name=self.name)
+ conditions = """where item_code = %(item_code)s and price_list = %(price_list)s and name != %(name)s"""
- for field in ['uom', 'valid_from',
- 'valid_upto', 'packing_unit', 'customer', 'supplier']:
+ for field in [
+ "uom",
+ "valid_from",
+ "valid_upto",
+ "packing_unit",
+ "customer",
+ "supplier",]:
if self.get(field):
- conditions += " and {0} = %({1})s".format(field, field)
- condition_data_dict[field] = self.get(field)
+ conditions += " and {0} = %({0})s ".format(field)
+ else:
+ conditions += "and (isnull({0}) or {0} = '')".format(field)
price_list_rate = frappe.db.sql("""
- SELECT price_list_rate
- FROM `tabItem Price`
- {conditions} """.format(conditions=conditions), condition_data_dict)
+ select price_list_rate
+ from `tabItem Price`
+ {conditions}
+ """.format(conditions=conditions),
+ self.as_dict(),)
- if price_list_rate :
- frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates."), ItemPriceDuplicateItem)
+ if price_list_rate:
+ frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
def before_save(self):
if self.selling:
self.reference = self.customer
if self.buying:
self.reference = self.supplier
-
+
if self.selling and not self.buying:
# if only selling then remove supplier
self.supplier = None
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index 702acc38fe..f3d406eeca 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -138,4 +138,23 @@ class TestItemPrice(unittest.TestCase):
# Valid price list must already exist
self.assertRaises(frappe.ValidationError, doc.save)
+ def test_empty_duplicate_validation(self):
+ # Check if none/empty values are not compared during insert validation
+ doc = frappe.copy_doc(test_records[2])
+ doc.customer = None
+ doc.price_list_rate = 21
+ doc.insert()
+
+ args = {
+ "price_list": doc.price_list,
+ "uom": "_Test UOM",
+ "transaction_date": '2017-04-18',
+ "qty": 7
+ }
+
+ price = get_price_list_rate_for(args, doc.item_code)
+ frappe.db.rollback()
+
+ self.assertEqual(price, 21)
+
test_records = frappe.get_test_records('Item Price')
diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
index f1e1fd3679..888bc2de47 100644
--- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
+++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
@@ -1,88 +1,57 @@
{
- "allow_copy": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "hash",
- "beta": 0,
- "creation": "2013-02-22 01:28:01",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "editable_grid": 1,
+ "actions": [],
+ "autoname": "hash",
+ "creation": "2013-02-22 01:28:01",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "specification",
+ "value",
+ "column_break_3",
+ "acceptance_formula"
+ ],
"fields": [
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "specification",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Parameter",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "specification",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "200px",
- "read_only": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0,
+ "fieldname": "specification",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Parameter",
+ "oldfieldname": "specification",
+ "oldfieldtype": "Data",
+ "print_width": "200px",
+ "reqd": 1,
"width": "200px"
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "value",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Acceptance Criteria",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "value",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "fieldname": "value",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Acceptance Criteria",
+ "oldfieldname": "value",
+ "oldfieldtype": "Data"
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "description": "Simple Python formula based on numeric Readings.
Example 1: reading_1 > 0.2 and reading_1 < 0.5
\nExample 2: (reading_1 + reading_2) / 2 < 10",
+ "fieldname": "acceptance_formula",
+ "fieldtype": "Code",
+ "in_list_view": 1,
+ "label": "Acceptance Criteria Formula"
}
- ],
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
-
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2016-07-11 03:28:01.074316",
- "modified_by": "Administrator",
- "module": "Stock",
- "name": "Item Quality Inspection Parameter",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "track_seen": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-11-16 16:33:42.421842",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Item Quality Inspection Parameter",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index bbfaeabaf7..749b13121d 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-21 16:16:39",
@@ -1120,7 +1121,7 @@
"idx": 261,
"is_submittable": 1,
"links": [],
- "modified": "2020-09-08 11:21:25.465966",
+ "modified": "2020-11-19 11:21:25.465966",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 1852985b1d..9dee9a95e4 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -581,6 +581,8 @@ def update_billing_percentage(pr_doc, update_modified=True):
@frappe.whitelist()
def make_purchase_invoice(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
+ from erpnext.accounts.party import get_payment_terms_template
+
doc = frappe.get_doc('Purchase Receipt', source_name)
returned_qty_map = get_returned_qty_map(source_name)
invoiced_qty_map = get_invoiced_qty_map(source_name)
@@ -591,6 +593,7 @@ def make_purchase_invoice(source_name, target_doc=None):
doc = frappe.get_doc(target)
doc.ignore_pricing_rule = 1
+ doc.payment_terms_template = get_payment_terms_template(source.supplier, "Supplier", source.company)
doc.run_method("onload")
doc.run_method("set_missing_values")
doc.run_method("calculate_taxes_and_totals")
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 722b2c9aea..9b8eeed1a1 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -42,6 +42,30 @@ class TestPurchaseReceipt(unittest.TestCase):
frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 1)
def test_make_purchase_invoice(self):
+ if not frappe.db.exists('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice'):
+ frappe.get_doc({
+ 'doctype': 'Payment Terms Template',
+ 'template_name': '_Test Payment Terms Template For Purchase Invoice',
+ 'allocate_payment_based_on_payment_terms': 1,
+ 'terms': [
+ {
+ 'doctype': 'Payment Terms Template Detail',
+ 'invoice_portion': 50.00,
+ 'credit_days_based_on': 'Day(s) after invoice date',
+ 'credit_days': 00
+ },
+ {
+ 'doctype': 'Payment Terms Template Detail',
+ 'invoice_portion': 50.00,
+ 'credit_days_based_on': 'Day(s) after invoice date',
+ 'credit_days': 30
+ }]
+ }).insert()
+
+ template = frappe.db.get_value('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice')
+ old_template_in_supplier = frappe.db.get_value("Supplier", "_Test Supplier", "payment_terms")
+ frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", template)
+
pr = make_purchase_receipt(do_not_save=True)
self.assertRaises(frappe.ValidationError, make_purchase_invoice, pr.name)
pr.submit()
@@ -51,10 +75,23 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertEqual(pi.doctype, "Purchase Invoice")
self.assertEqual(len(pi.get("items")), len(pr.get("items")))
- # modify rate
+ # test maintaining same rate throughout purchade cycle
pi.get("items")[0].rate = 200
self.assertRaises(frappe.ValidationError, frappe.get_doc(pi).submit)
+ # test if payment terms are fetched and set in PI
+ self.assertEqual(pi.payment_terms_template, template)
+ self.assertEqual(pi.payment_schedule[0].payment_amount, flt(pi.grand_total)/2)
+ self.assertEqual(pi.payment_schedule[0].invoice_portion, 50)
+ self.assertEqual(pi.payment_schedule[1].payment_amount, flt(pi.grand_total)/2)
+ self.assertEqual(pi.payment_schedule[1].invoice_portion, 50)
+
+ # teardown
+ pi.delete() # draft PI
+ pr.cancel()
+ frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", old_template_in_supplier)
+ frappe.get_doc('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice').delete()
+
def test_purchase_receipt_no_gl_entry(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
index c3bb514184..399a63a186 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
@@ -4,15 +4,20 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from frappe.model.mapper import get_mapped_doc
+from frappe import _
+from frappe.utils import flt
from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \
import get_template_details
-from frappe.model.mapper import get_mapped_doc
class QualityInspection(Document):
def validate(self):
if not self.readings and self.item_code:
self.get_item_specification_details()
+ if self.readings:
+ self.set_status_based_on_acceptance_formula()
+
def get_item_specification_details(self):
if not self.quality_inspection_template:
self.quality_inspection_template = frappe.db.get_value('Item',
@@ -26,6 +31,7 @@ class QualityInspection(Document):
child = self.append('readings', {})
child.specification = d.specification
child.value = d.value
+ child.acceptance_formula = d.acceptance_formula
child.status = "Accepted"
def get_quality_inspection_template(self):
@@ -58,6 +64,29 @@ class QualityInspection(Document):
.format(parent_doc=self.reference_type, child_doc=doctype),
(quality_inspection, self.modified, self.reference_name, self.item_code))
+ def set_status_based_on_acceptance_formula(self):
+ for reading in self.readings:
+ if not reading.acceptance_formula: continue
+
+ condition = reading.acceptance_formula
+ data = {}
+ for i in range(1, 11):
+ field = "reading_" + str(i)
+ data[field] = flt(reading.get(field)) or 0
+
+ try:
+ result = frappe.safe_eval(condition, None, data)
+ reading.status = "Accepted" if result else "Rejected"
+ except SyntaxError:
+ frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx),
+ title=_("Invalid Formula"))
+ except NameError as e:
+ field = frappe.bold(e.args[0].split()[1])
+ frappe.throw(_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.")
+ .format(reading.idx, field),
+ title=_("Invalid Formula"))
+
+
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def item_query(doctype, txt, searchfield, start, page_len, filters):
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index bb535c1f6a..2c40009426 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -7,6 +7,7 @@ import unittest
from frappe.utils import nowdate
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError, QualityInspectionNotSubmittedError
# test_records = frappe.get_test_records('Quality Inspection')
@@ -17,10 +18,12 @@ class TestQualityInspection(unittest.TestCase):
frappe.db.set_value("Item", "_Test Item with QA", "inspection_required_before_delivery", 1)
def test_qa_for_delivery(self):
+ make_stock_entry(item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100)
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
+
self.assertRaises(QualityInspectionRequiredError, dn.submit)
- qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected", submit=True)
+ qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected")
dn.reload()
self.assertRaises(QualityInspectionRejectedError, dn.submit)
@@ -28,12 +31,51 @@ class TestQualityInspection(unittest.TestCase):
dn.reload()
dn.submit()
+ qa.cancel()
+ dn.reload()
+ dn.cancel()
+
def test_qa_not_submit(self):
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
- qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, submit = False)
+ qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True)
dn.items[0].quality_inspection = qa.name
self.assertRaises(QualityInspectionNotSubmittedError, dn.submit)
+ qa.delete()
+ dn.delete()
+
+ def test_formula_based_qi_readings(self):
+ dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
+ readings = [{
+ "specification": "Iron Content",
+ "acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
+ "reading_1": 0.4
+ },
+ {
+ "specification": "Calcium Content",
+ "acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
+ "reading_1": 0.7
+ },
+ {
+ "specification": "Mg Content",
+ "acceptance_formula": "(reading_1 + reading_2 + reading_3) / 3 < 0.9",
+ "reading_1": 0.5,
+ "reading_2": 0.7,
+ "reading_3": "random text" # check if random string input causes issues
+ }]
+
+ qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name,
+ readings=readings, do_not_save=True)
+ qa.save()
+
+ # status must be auto set as per formula
+ self.assertEqual(qa.readings[0].status, "Accepted")
+ self.assertEqual(qa.readings[1].status, "Rejected")
+ self.assertEqual(qa.readings[2].status, "Accepted")
+
+ qa.delete()
+ dn.delete()
+
def create_quality_inspection(**args):
args = frappe._dict(args)
qa = frappe.new_doc("Quality Inspection")
@@ -44,12 +86,18 @@ def create_quality_inspection(**args):
qa.item_code = args.item_code or "_Test Item with QA"
qa.sample_size = 1
qa.inspected_by = frappe.session.user
- qa.append("readings", {
- "specification": "Size",
- "status": args.status
- })
- qa.save()
- if args.submit:
- qa.submit()
+
+ readings = args.readings or {"specification": "Size", "status": args.status}
+
+ if isinstance(readings, list):
+ for entry in readings:
+ qa.append("readings", entry)
+ else:
+ qa.append("readings", readings)
+
+ if not args.do_not_save:
+ qa.save()
+ if not args.do_not_submit:
+ qa.submit()
return qa
diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
index f9f8a71c02..c1976dd1fb 100644
--- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
+++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
@@ -1,22 +1,29 @@
{
+ "actions": [],
"autoname": "hash",
"creation": "2013-02-22 01:27:43",
"doctype": "DocType",
"editable_grid": 1,
+ "engine": "InnoDB",
"field_order": [
"specification",
"value",
+ "status",
+ "column_break_4",
+ "acceptance_formula",
+ "section_break_3",
"reading_1",
"reading_2",
"reading_3",
+ "column_break_10",
"reading_4",
"reading_5",
"reading_6",
+ "column_break_14",
"reading_7",
"reading_8",
"reading_9",
- "reading_10",
- "status"
+ "reading_10"
],
"fields": [
{
@@ -124,15 +131,40 @@
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "Accepted\nRejected"
+ },
+ {
+ "fieldname": "section_break_3",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "description": "Simple Python formula based on numeric Readings.
Example 1: reading_1 > 0.2 and reading_1 < 0.5
\nExample 2: (reading_1 + reading_2) / 2 < 10",
+ "fieldname": "acceptance_formula",
+ "fieldtype": "Code",
+ "label": "Acceptance Criteria Formula"
+ },
+ {
+ "fieldname": "column_break_10",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "column_break_14",
+ "fieldtype": "Column Break"
}
],
"idx": 1,
"istable": 1,
- "modified": "2019-07-11 18:48:12.667404",
+ "links": [],
+ "modified": "2020-11-16 16:34:29.947856",
"modified_by": "Administrator",
"module": "Stock",
"name": "Quality Inspection Reading",
"owner": "Administrator",
"permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
index 0d9a90312b..e2848469b8 100644
--- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
+++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
@@ -12,5 +12,7 @@ class QualityInspectionTemplate(Document):
def get_template_details(template):
if not template: return []
- return frappe.get_all('Item Quality Inspection Parameter', fields=["specification", "value"],
- filters={'parenttype': 'Quality Inspection Template', 'parent': template}, order_by="idx")
\ No newline at end of file
+ return frappe.get_all('Item Quality Inspection Parameter',
+ fields=["specification", "value", "acceptance_formula"],
+ filters={'parenttype': 'Quality Inspection Template', 'parent': template},
+ order_by="idx")
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 768526705c..e3159b95c3 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1227,8 +1227,6 @@ class StockEntry(StockController):
return item_dict
def add_to_stock_entry_detail(self, item_dict, bom_no=None):
- cost_center = frappe.db.get_value("Company", self.company, 'cost_center')
-
for d in item_dict:
stock_uom = item_dict[d].get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
@@ -1239,9 +1237,10 @@ class StockEntry(StockController):
se_child.uom = item_dict[d]["uom"] if item_dict[d].get("uom") else stock_uom
se_child.stock_uom = stock_uom
se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty"))
- se_child.cost_center = item_dict[d].get("cost_center") or cost_center
se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
se_child.subcontracted_item = item_dict[d].get("main_item_code")
+ se_child.cost_center = (item_dict[d].get("cost_center") or
+ get_default_cost_center(item_dict[d], company = self.company))
for field in ["idx", "po_detail", "original_item",
"expense_account", "description", "item_name"]:
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 8d8dcb74c3..08f7a83b89 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -559,23 +559,40 @@ def get_default_deferred_account(args, item, fieldname=None):
else:
return None
-def get_default_cost_center(args, item, item_group, brand, company=None):
+def get_default_cost_center(args, item=None, item_group=None, brand=None, company=None):
cost_center = None
+
+ if not company and args.get("company"):
+ company = args.get("company")
+
if args.get('project'):
cost_center = frappe.db.get_value("Project", args.get("project"), "cost_center", cache=True)
- if not cost_center:
+ if not cost_center and (item and item_group and brand):
if args.get('customer'):
cost_center = item.get('selling_cost_center') or item_group.get('selling_cost_center') or brand.get('selling_cost_center')
else:
cost_center = item.get('buying_cost_center') or item_group.get('buying_cost_center') or brand.get('buying_cost_center')
- cost_center = cost_center or args.get("cost_center")
+ elif not cost_center and args.get("item_code") and company:
+ for method in ["get_item_defaults", "get_item_group_defaults", "get_brand_defaults"]:
+ path = "erpnext.stock.get_item_details.{0}".format(method)
+ data = frappe.get_attr(path)(args.get("item_code"), company)
+
+ if data and (data.selling_cost_center or data.buying_cost_center):
+ return data.selling_cost_center or data.buying_cost_center
+
+ if not cost_center and args.get("cost_center"):
+ cost_center = args.get("cost_center")
if (company and cost_center
and frappe.get_cached_value("Cost Center", cost_center, "company") != company):
return None
+ if not cost_center and company:
+ cost_center = frappe.get_cached_value("Company",
+ company, "cost_center")
+
return cost_center
def get_default_supplier(args, item, item_group, brand):
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 3dc806fb43..8aaf7abcbe 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -20,7 +20,8 @@ def execute(filters=None):
fifo_queue = sorted(filter(_func, item_dict["fifo_queue"]), key=_func)
details = item_dict["details"]
- if not fifo_queue and (not item_dict.get("total_qty")): continue
+
+ if not fifo_queue: continue
average_age = get_average_age(fifo_queue, to_date)
earliest_age = date_diff(to_date, fifo_queue[0][1])
diff --git a/erpnext/translations/af.csv b/erpnext/translations/af.csv
index 07f81e9979..45435d8600 100644
--- a/erpnext/translations/af.csv
+++ b/erpnext/translations/af.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Laai af as Json,
End date can not be less than start date,Einddatum kan nie minder wees as die begin datum nie,
For Default Supplier (Optional),Vir Standaardverskaffer (opsioneel),
From date cannot be greater than To date,Vanaf datum kan nie groter wees as Datum,
-Get items from,Kry items van,
Group by,Groep By,
In stock,In voorraad,
Item name,Item naam,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Aankooporders,
Purchase Receipt Trends,Aankoopontvangstendense,
Purchase Register,Aankoopregister,
Quotation Trends,Aanhalingstendense,
-Quoted Item Comparison,Genoteerde Item Vergelyking,
Received Items To Be Billed,Items ontvang om gefaktureer te word,
Qty to Order,Hoeveelheid om te bestel,
Requested Items To Be Transferred,Gevraagde items wat oorgedra moet word,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Die item waarna verwys word
Therapy Session overlaps with {0},Terapiesessie oorvleuel met {0},
Therapy Sessions Overlapping,Terapiesessies oorvleuel,
Therapy Plans,Terapieplanne,
+"Item Code, warehouse, quantity are required on row {0}","Itemkode, pakhuis, hoeveelheid word in ry {0} vereis",
+Get Items from Material Requests against this Supplier,Kry items uit materiaalversoeke teen hierdie verskaffer,
+Enable European Access,Aktiveer Europese toegang,
+Creating Purchase Order ...,Skep tans bestelling ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Kies 'n verskaffer uit die verstekverskaffers van die onderstaande items. By seleksie sal 'n bestelling slegs gemaak word teen items wat tot die geselekteerde verskaffer behoort.,
+Row #{}: You must select {} serial numbers for item {}.,Ry # {}: u moet {} reeksnommers vir item {} kies.,
diff --git a/erpnext/translations/am.csv b/erpnext/translations/am.csv
index 1d0533fd99..554b0a54f1 100644
--- a/erpnext/translations/am.csv
+++ b/erpnext/translations/am.csv
@@ -4238,7 +4238,6 @@ Download as JSON,እንደ ጆንሰን አውርድ ፡፡,
End date can not be less than start date,የማብቂያ ቀን ከመጀመሪያ ቀን ያነሰ መሆን አይችልም,
For Default Supplier (Optional),ነባሪ አቅራቢ (አማራጭ),
From date cannot be greater than To date,ቀን ቀን ወደ በላይ ሊሆን አይችልም ከ,
-Get items from,ከ ንጥሎችን ያግኙ,
Group by,ቡድን በ,
In stock,ለሽያጭ የቀረበ እቃ,
Item name,ንጥል ስም,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,ትዕዛዝ በመታየት ላይ ይግዙ,
Purchase Receipt Trends,የግዢ ደረሰኝ በመታየት ላይ ያሉ,
Purchase Register,የግዢ ይመዝገቡ,
Quotation Trends,በትዕምርተ ጥቅስ አዝማሚያዎች,
-Quoted Item Comparison,የተጠቀሰ ንጥል ንጽጽር,
Received Items To Be Billed,ተቀብሏል ንጥሎች እንዲከፍሉ ለማድረግ,
Qty to Order,ለማዘዝ ብዛት,
Requested Items To Be Transferred,ተጠይቋል ንጥሎች መወሰድ,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,የተጠቀሰው ንጥ
Therapy Session overlaps with {0},ቴራፒ ክፍለ-ጊዜ ከ {0} ጋር ይደራረባል,
Therapy Sessions Overlapping,ቴራፒ ክፍለ-ጊዜዎች መደራረብ,
Therapy Plans,የሕክምና ዕቅዶች,
+"Item Code, warehouse, quantity are required on row {0}",የእቃ ኮድ ፣ መጋዘን ፣ ብዛት በረድፍ {0} ላይ ያስፈልጋሉ,
+Get Items from Material Requests against this Supplier,በዚህ አቅራቢ ላይ እቃዎችን ከቁሳዊ ጥያቄዎች ያግኙ,
+Enable European Access,የአውሮፓ መዳረሻን ያንቁ,
+Creating Purchase Order ...,የግዢ ትዕዛዝ በመፍጠር ላይ ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",ከዚህ በታች ካሉ ዕቃዎች ነባሪ አቅራቢዎች አቅራቢ ይምረጡ። በምርጫ ወቅት ለተመረጠው አቅራቢ ብቻ በሆኑ ዕቃዎች ላይ የግዢ ትዕዛዝ ይደረጋል።,
+Row #{}: You must select {} serial numbers for item {}.,ረድፍ # {}: {} ለንጥል ተከታታይ ቁጥሮች {} መምረጥ አለብዎት።,
diff --git a/erpnext/translations/ar.csv b/erpnext/translations/ar.csv
index de75e893c7..91a9da9f16 100644
--- a/erpnext/translations/ar.csv
+++ b/erpnext/translations/ar.csv
@@ -4238,7 +4238,6 @@ Download as JSON,تنزيل باسم Json,
End date can not be less than start date,تاريخ النهاية لا يمكن أن يكون اقل من تاريخ البدء\n
\nEnd Date can not be less than Start Date,
For Default Supplier (Optional),للمورد الافتراضي (اختياري),
From date cannot be greater than To date,(من تاريخ) لا يمكن أن يكون أكبر (الي التاريخ),
-Get items from,الحصول على البنود من,
Group by,المجموعة حسب,
In stock,في المخزن,
Item name,اسم السلعة,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,اتجهات امر الشراء,
Purchase Receipt Trends,شراء اتجاهات الإيصال,
Purchase Register,سجل شراء,
Quotation Trends,مؤشرات المناقصة,
-Quoted Item Comparison,مقارنة بند المناقصة,
Received Items To Be Billed,العناصر الواردة إلى أن توصف,
Qty to Order,الكمية للطلب,
Requested Items To Be Transferred,العناصر المطلوبة على أن يتم تحويلها,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,العنصر المشار
Therapy Session overlaps with {0},تتداخل جلسة العلاج مع {0},
Therapy Sessions Overlapping,جلسات العلاج متداخلة,
Therapy Plans,خطط العلاج,
+"Item Code, warehouse, quantity are required on row {0}",مطلوب رمز الصنف والمستودع والكمية في الصف {0},
+Get Items from Material Requests against this Supplier,الحصول على عناصر من طلبات المواد ضد هذا المورد,
+Enable European Access,تمكين الوصول الأوروبي,
+Creating Purchase Order ...,إنشاء أمر شراء ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",حدد موردًا من الموردين الافتراضيين للعناصر أدناه. عند التحديد ، سيتم إجراء طلب الشراء مقابل العناصر التي تنتمي إلى المورد المحدد فقط.,
+Row #{}: You must select {} serial numbers for item {}.,الصف # {}: يجب تحديد {} الأرقام التسلسلية للعنصر {}.,
diff --git a/erpnext/translations/bg.csv b/erpnext/translations/bg.csv
index 79be6dfc19..15278a6a40 100644
--- a/erpnext/translations/bg.csv
+++ b/erpnext/translations/bg.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Изтеглете като JSON,
End date can not be less than start date,Крайна дата не може да бъде по-малка от началната дата,
For Default Supplier (Optional),За доставчик по подразбиране (по избор),
From date cannot be greater than To date,От дата не може да бъде по-голямо от до дата,
-Get items from,Вземете елементи от,
Group by,Групирай по,
In stock,В наличност,
Item name,Име на артикул,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Поръчката Trends,
Purchase Receipt Trends,Покупка Квитанция Trends,
Purchase Register,Покупка Регистрация,
Quotation Trends,Оферта Тенденции,
-Quoted Item Comparison,Сравнение на редове от оферти,
Received Items To Be Billed,"Приети артикули, които да се фактирират",
Qty to Order,Количество към поръчка,
Requested Items To Be Transferred,Желани артикули да бъдат прехвърлени,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Позицията, пос
Therapy Session overlaps with {0},Терапевтичната сесия се припокрива с {0},
Therapy Sessions Overlapping,Терапевтични сесии Припокриване,
Therapy Plans,Планове за терапия,
+"Item Code, warehouse, quantity are required on row {0}","Код на артикул, склад, количество се изискват на ред {0}",
+Get Items from Material Requests against this Supplier,Вземете артикули от заявки за материали срещу този доставчик,
+Enable European Access,Активирайте европейския достъп,
+Creating Purchase Order ...,Създаване на поръчка ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Изберете доставчик от доставчиците по подразбиране на елементите по-долу. При избора ще бъде направена Поръчка за покупка срещу артикули, принадлежащи само на избрания Доставчик.",
+Row #{}: You must select {} serial numbers for item {}.,Ред № {}: Трябва да изберете {} серийни номера за артикул {}.,
diff --git a/erpnext/translations/bn.csv b/erpnext/translations/bn.csv
index 1e7413b1cd..cf0971667b 100644
--- a/erpnext/translations/bn.csv
+++ b/erpnext/translations/bn.csv
@@ -4238,7 +4238,6 @@ Download as JSON,জসন হিসাবে ডাউনলোড করুন
End date can not be less than start date,শেষ তারিখ শুরু তারিখ থেকে কম হতে পারে না,
For Default Supplier (Optional),ডিফল্ট সরবরাহকারীর জন্য (ঐচ্ছিক),
From date cannot be greater than To date,তারিখ থেকে তারিখের চেয়ে বেশি হতে পারে না,
-Get items from,থেকে আইটেম পান,
Group by,গ্রুপ দ্বারা,
In stock,স্টক ইন,
Item name,আইটেম নাম,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,অর্ডার প্রবণতা ক্রয়,
Purchase Receipt Trends,কেনার রসিদ প্রবণতা,
Purchase Register,ক্রয় নিবন্ধন,
Quotation Trends,উদ্ধৃতি প্রবণতা,
-Quoted Item Comparison,উদ্ধৃত আইটেম তুলনা,
Received Items To Be Billed,গৃহীত চলছে বিল তৈরি করা,
Qty to Order,অর্ডার Qty,
Requested Items To Be Transferred,অনুরোধ করা চলছে স্থানান্তর করা,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1} দ্বার
Therapy Session overlaps with {0},থেরাপি সেশন {0 with দিয়ে ওভারল্যাপ করে,
Therapy Sessions Overlapping,থেরাপি সেশনস ওভারল্যাপিং,
Therapy Plans,থেরাপি পরিকল্পনা,
+"Item Code, warehouse, quantity are required on row {0}","আইটেম কোড, গুদাম, পরিমাণ সারিতে প্রয়োজন {0}",
+Get Items from Material Requests against this Supplier,এই সরবরাহকারীর বিরুদ্ধে উপাদান অনুরোধগুলি থেকে আইটেমগুলি পান,
+Enable European Access,ইউরোপীয় অ্যাক্সেস সক্ষম করুন,
+Creating Purchase Order ...,ক্রয় ক্রম তৈরি করা হচ্ছে ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","নীচের আইটেমগুলির ডিফল্ট সরবরাহকারী থেকে কোনও সরবরাহকারী নির্বাচন করুন। নির্বাচনের সময়, কেবলমাত্র নির্বাচিত সরবরাহকারীর অন্তর্ভুক্ত আইটেমগুলির বিরুদ্ধে ক্রয় আদেশ দেওয়া হবে।",
+Row #{}: You must select {} serial numbers for item {}.,সারি # {}: আইটেমের জন্য আপনাকে অবশ্যই}} ক্রমিক সংখ্যা নির্বাচন করতে হবে {}।,
diff --git a/erpnext/translations/bs.csv b/erpnext/translations/bs.csv
index 4fec5e0cc8..6ef445a1af 100644
--- a/erpnext/translations/bs.csv
+++ b/erpnext/translations/bs.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Preuzmi kao JSON,
End date can not be less than start date,Datum završetka ne može biti manja od početnog datuma,
For Default Supplier (Optional),Za podrazumevani dobavljač,
From date cannot be greater than To date,Od datuma ne može biti veća od To Date,
-Get items from,Get stavke iz,
Group by,Group By,
In stock,Na zalihama,
Item name,Naziv artikla,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Trendovi narudžbenica kupnje,
Purchase Receipt Trends,Račun kupnje trendovi,
Purchase Register,Kupnja Registracija,
Quotation Trends,Trendovi ponude,
-Quoted Item Comparison,Citirano Stavka Poređenje,
Received Items To Be Billed,Primljeni Proizvodi se naplaćuje,
Qty to Order,Količina za narudžbu,
Requested Items To Be Transferred,Traženi stavki za prijenos,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Stavka na koju se poziva {0
Therapy Session overlaps with {0},Sjednica terapije preklapa se sa {0},
Therapy Sessions Overlapping,Preklapanje terapijskih sesija,
Therapy Plans,Planovi terapije,
+"Item Code, warehouse, quantity are required on row {0}","Šifra artikla, skladište, količina su obavezni na retku {0}",
+Get Items from Material Requests against this Supplier,Nabavite predmete od materijalnih zahtjeva protiv ovog dobavljača,
+Enable European Access,Omogućiti evropski pristup,
+Creating Purchase Order ...,Kreiranje narudžbenice ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Odaberite dobavljača od zadanih dobavljača dolje navedenih stavki. Nakon odabira, narudžbenica će se izvršiti samo za proizvode koji pripadaju odabranom dobavljaču.",
+Row #{}: You must select {} serial numbers for item {}.,Red # {}: Morate odabrati {} serijske brojeve za stavku {}.,
diff --git a/erpnext/translations/ca.csv b/erpnext/translations/ca.csv
index 58e6edb0a8..18fa52a2b7 100644
--- a/erpnext/translations/ca.csv
+++ b/erpnext/translations/ca.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Descarregueu com a Json,
End date can not be less than start date,Data de finalització no pot ser inferior a data d'inici,
For Default Supplier (Optional),Per proveïdor predeterminat (opcional),
From date cannot be greater than To date,Des de la data no pot ser superior a la data,
-Get items from,Obtenir articles de,
Group by,Agrupar per,
In stock,En estoc,
Item name,Nom de l'article,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Compra Tendències Sol·licitar,
Purchase Receipt Trends,Purchase Receipt Trends,
Purchase Register,Compra de Registre,
Quotation Trends,Quotation Trends,
-Quoted Item Comparison,Citat article Comparació,
Received Items To Be Billed,Articles rebuts per a facturar,
Qty to Order,Quantitat de comanda,
Requested Items To Be Transferred,Articles sol·licitats per a ser transferits,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,L'element a què fa ref
Therapy Session overlaps with {0},La sessió de teràpia es coincideix amb {0},
Therapy Sessions Overlapping,Sessions de teràpia superposades,
Therapy Plans,Plans de teràpia,
+"Item Code, warehouse, quantity are required on row {0}","El codi de l'article, el magatzem, la quantitat són obligatoris a la fila {0}",
+Get Items from Material Requests against this Supplier,Obteniu articles de sol·licituds de material contra aquest proveïdor,
+Enable European Access,Activa l'accés europeu,
+Creating Purchase Order ...,S'està creant una comanda de compra ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Seleccioneu un proveïdor dels proveïdors predeterminats dels articles següents. En seleccionar-lo, es farà una Comanda de compra únicament contra articles pertanyents al Proveïdor seleccionat.",
+Row #{}: You must select {} serial numbers for item {}.,Fila núm. {}: Heu de seleccionar {} números de sèrie de l'element {}.,
diff --git a/erpnext/translations/cs.csv b/erpnext/translations/cs.csv
index 43fd472b41..705e471d27 100644
--- a/erpnext/translations/cs.csv
+++ b/erpnext/translations/cs.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Stáhnout jako JSON,
End date can not be less than start date,Datum ukončení nesmí být menší než datum zahájení,
For Default Supplier (Optional),Výchozí dodavatel (volitelné),
From date cannot be greater than To date,Od Datum nemůže být větší než Datum,
-Get items from,Položka získaná z,
Group by,Seskupit podle,
In stock,Na skladě,
Item name,Název položky,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Nákupní objednávka trendy,
Purchase Receipt Trends,Doklad o koupi Trendy,
Purchase Register,Nákup Register,
Quotation Trends,Uvozovky Trendy,
-Quoted Item Comparison,Citoval Položka Porovnání,
Received Items To Be Billed,"Přijaté položek, které mají být účtovány",
Qty to Order,Množství k objednávce,
Requested Items To Be Transferred,Požadované položky mají být převedeny,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Položka, na kterou odkazu
Therapy Session overlaps with {0},Terapie se překrývá s {0},
Therapy Sessions Overlapping,Terapeutické relace se překrývají,
Therapy Plans,Terapeutické plány,
+"Item Code, warehouse, quantity are required on row {0}","Na řádku {0} je vyžadován kód položky, sklad, množství",
+Get Items from Material Requests against this Supplier,Získejte položky z materiálových požadavků vůči tomuto dodavateli,
+Enable European Access,Povolit evropský přístup,
+Creating Purchase Order ...,Vytváření nákupní objednávky ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Vyberte dodavatele z výchozích dodavatelů níže uvedených položek. Při výběru bude provedena objednávka pouze na položky patřící vybranému dodavateli.,
+Row #{}: You must select {} serial numbers for item {}.,Řádek # {}: Musíte vybrat {} sériová čísla pro položku {}.,
diff --git a/erpnext/translations/da.csv b/erpnext/translations/da.csv
index 7222daf77f..c0d0146694 100644
--- a/erpnext/translations/da.csv
+++ b/erpnext/translations/da.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Download som JSON,
End date can not be less than start date,Slutdato kan ikke være mindre end startdato,
For Default Supplier (Optional),For standardleverandør (valgfrit),
From date cannot be greater than To date,Fra dato ikke kan være større end til dato,
-Get items from,Hent varer fra,
Group by,Sortér efter,
In stock,På lager,
Item name,Varenavn,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Indkøbsordre Trends,
Purchase Receipt Trends,Købskvittering Tendenser,
Purchase Register,Indkøb Register,
Quotation Trends,Tilbud trends,
-Quoted Item Comparison,Sammenligning Citeret Vare,
Received Items To Be Billed,Modtagne varer skal faktureres,
Qty to Order,Antal til ordre,
Requested Items To Be Transferred,"Anmodet Varer, der skal overføres",
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Den vare, der henvises til
Therapy Session overlaps with {0},Terapisession overlapper med {0},
Therapy Sessions Overlapping,Terapisessioner overlapper hinanden,
Therapy Plans,Terapiplaner,
+"Item Code, warehouse, quantity are required on row {0}","Varekode, lager, antal kræves i række {0}",
+Get Items from Material Requests against this Supplier,Få varer fra materialeanmodninger mod denne leverandør,
+Enable European Access,Aktiver europæisk adgang,
+Creating Purchase Order ...,Opretter indkøbsordre ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Vælg en leverandør fra standardleverandørerne af nedenstående varer. Ved valg foretages en indkøbsordre kun mod varer, der tilhører den valgte leverandør.",
+Row #{}: You must select {} serial numbers for item {}.,Række nr. {}: Du skal vælge {} serienumre for varen {}.,
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index b65c9436ef..ca03a787cd 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Als JSON herunterladen,
End date can not be less than start date,Das Enddatum darf nicht kleiner als das Startdatum sein,
For Default Supplier (Optional),Für Standardlieferanten (optional),
From date cannot be greater than To date,Das Ab-Datum kann nicht größer als das Bis-Datum sein,
-Get items from,Holen Sie Elemente aus,
Group by,Gruppieren nach,
In stock,Auf Lager,
Item name,Artikelname,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Entwicklung Lieferantenaufträge,
Purchase Receipt Trends,Trendanalyse Kaufbelege,
Purchase Register,Übersicht über Einkäufe,
Quotation Trends,Trendanalyse Angebote,
-Quoted Item Comparison,Vergleich angebotener Artikel,
Received Items To Be Billed,"Von Lieferanten gelieferte Artikel, die noch abgerechnet werden müssen",
Qty to Order,Zu bestellende Menge,
Requested Items To Be Transferred,"Angeforderte Artikel, die übertragen werden sollen",
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Der Artikel, auf den {0} -
Therapy Session overlaps with {0},Die Therapiesitzung überschneidet sich mit {0},
Therapy Sessions Overlapping,Überlappende Therapiesitzungen,
Therapy Plans,Therapiepläne,
+"Item Code, warehouse, quantity are required on row {0}","Artikelcode, Lager, Menge sind in Zeile {0} erforderlich.",
+Get Items from Material Requests against this Supplier,Erhalten Sie Artikel aus Materialanfragen gegen diesen Lieferanten,
+Enable European Access,Ermöglichen Sie den europäischen Zugang,
+Creating Purchase Order ...,Bestellung anlegen ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Wählen Sie einen Lieferanten aus den Standardlieferanten der folgenden Artikel aus. Bei der Auswahl erfolgt eine Bestellung nur für Artikel, die dem ausgewählten Lieferanten gehören.",
+Row #{}: You must select {} serial numbers for item {}.,Zeile # {}: Sie müssen {} Seriennummern für Artikel {} auswählen.,
diff --git a/erpnext/translations/el.csv b/erpnext/translations/el.csv
index 2db4ac8c1c..acf5db5d46 100644
--- a/erpnext/translations/el.csv
+++ b/erpnext/translations/el.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Λήψη ως JSON,
End date can not be less than start date,Η ημερομηνία λήξης δεν μπορεί να είναι προγενέστερη της ημερομηνίας έναρξης,
For Default Supplier (Optional),Για προεπιλεγμένο προμηθευτή (προαιρετικό),
From date cannot be greater than To date,Από την ημερομηνία αυτή δεν μπορεί να είναι μεταγενέστερη από την έως ημερομηνία,
-Get items from,Πάρτε τα στοιχεία από,
Group by,Ομαδοποίηση κατά,
In stock,Σε απόθεμα,
Item name,Όνομα είδους,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Τάσεις παραγγελίας αγοράς,
Purchase Receipt Trends,Τάσεις αποδεικτικού παραλαβής αγοράς,
Purchase Register,Ταμείο αγορών,
Quotation Trends,Τάσεις προσφορών,
-Quoted Item Comparison,Εισηγμένες Στοιχείο Σύγκριση,
Received Items To Be Billed,Είδη που παραλήφθηκαν και πρέπει να τιμολογηθούν,
Qty to Order,Ποσότητα για παραγγελία,
Requested Items To Be Transferred,Είδη που ζητήθηκε να μεταφερθούν,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Το στοιχείο πο
Therapy Session overlaps with {0},Η συνεδρία θεραπείας αλληλεπικαλύπτεται με {0},
Therapy Sessions Overlapping,Συνεδρίες συνεδρίας,
Therapy Plans,Σχέδια θεραπείας,
+"Item Code, warehouse, quantity are required on row {0}","Κωδικός είδους, αποθήκη, ποσότητα απαιτείται στη σειρά {0}",
+Get Items from Material Requests against this Supplier,Λάβετε στοιχεία από αιτήματα υλικών έναντι αυτού του προμηθευτή,
+Enable European Access,Ενεργοποίηση ευρωπαϊκής πρόσβασης,
+Creating Purchase Order ...,Δημιουργία παραγγελίας αγοράς ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Επιλέξτε έναν προμηθευτή από τους προεπιλεγμένους προμηθευτές των παρακάτω στοιχείων. Κατά την επιλογή, μια εντολή αγοράς θα πραγματοποιείται έναντι αντικειμένων που ανήκουν στον επιλεγμένο Προμηθευτή μόνο.",
+Row #{}: You must select {} serial numbers for item {}.,Σειρά # {}: Πρέπει να επιλέξετε {} σειριακούς αριθμούς για το στοιχείο {}.,
diff --git a/erpnext/translations/es.csv b/erpnext/translations/es.csv
index be195aad11..0f2259db21 100644
--- a/erpnext/translations/es.csv
+++ b/erpnext/translations/es.csv
@@ -421,7 +421,7 @@ Buildings,Edificios,
Bundle items at time of sale.,Agrupe elementos al momento de la venta.,
Business Development Manager,Gerente de Desarrollo de Negocios,
Buy,Comprar,
-Buying,Comprando,
+Buying,Compras,
Buying Amount,Importe de compra,
Buying Price List,Lista de precios de compra,
Buying Rate,Tipo de Cambio de Compra,
@@ -535,7 +535,7 @@ City/Town,Ciudad / Provincia,
Claimed Amount,Cantidad reclamada,
Clay,Arcilla,
Clear filters,Filtros claros,
-Clear values,Valores claros,
+Clear values,Quitar valores,
Clearance Date,Fecha de liquidación,
Clearance Date not mentioned,Fecha de liquidación no definida,
Clearance Date updated,Fecha de liquidación actualizada,
@@ -728,7 +728,7 @@ Current Liabilities,Pasivo circulante,
Current Qty,Cant. Actual,
Current invoice {0} is missing,La factura actual {0} falta,
Custom HTML,HTML Personalizado,
-Custom?,Personalizado?,
+Custom?,¿Personalizado?,
Customer,Cliente,
Customer Addresses And Contacts,Direcciones de clientes y contactos,
Customer Contact,Contacto del Cliente,
@@ -783,7 +783,7 @@ Default Activity Cost exists for Activity Type - {0},Existe una actividad de cos
Default BOM ({0}) must be active for this item or its template,La lista de materiales (LdM) por defecto ({0}) debe estar activa para este producto o plantilla,
Default BOM for {0} not found,BOM por defecto para {0} no encontrado,
Default BOM not found for Item {0} and Project {1},La lista de materiales predeterminada no se encontró para el Elemento {0} y el Proyecto {1},
-Default Letter Head,Encabezado predeterminado,
+Default Letter Head,Encabezado Predeterminado,
Default Tax Template,Plantilla de impuesto predeterminado,
Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.,Unidad de medida predeterminada para el artículo {0} no se puede cambiar directamente porque ya ha realizado alguna transacción (s) con otra UOM. Usted tendrá que crear un nuevo elemento a utilizar un UOM predeterminado diferente.,
Default Unit of Measure for Variant '{0}' must be same as in Template '{1}',Unidad de medida predeterminada para variante '{0}' debe ser la mismo que en la plantilla '{1}',
@@ -966,7 +966,7 @@ Equity,Patrimonio,
Error Log,Registro de Errores,
Error evaluating the criteria formula,Error al evaluar la fórmula de criterios,
Error in formula or condition: {0},Error Fórmula o Condición: {0},
-Error: Not a valid id?,Error: No es un ID válido?,
+Error: Not a valid id?,Error: ¿No es un ID válido?,
Estimated Cost,Costo estimado,
Evaluation,Evaluación,
"Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:","Incluso si hay varias reglas de precios con mayor prioridad, se aplican entonces siguientes prioridades internas:",
@@ -1338,7 +1338,7 @@ Issue Material,Distribuir materiales,
Issued,Emitido,
Issues,Incidencias,
It is needed to fetch Item Details.,Se necesita a buscar Detalles del artículo.,
-Item,Productos,
+Item,Producto,
Item 1,Elemento 1,
Item 2,Elemento 2,
Item 3,Elemento 3,
@@ -1426,7 +1426,7 @@ Last Purchase Price,Último precio de compra,
Last Purchase Rate,Tasa de cambio de última compra,
Latest,Más reciente,
Latest price updated in all BOMs,Último precio actualizado en todas las Listas de Materiales,
-Lead,Dirigir,
+Lead,Iniciativa,
Lead Count,Cuenta de Iniciativa,
Lead Owner,Propietario de la iniciativa,
Lead Owner cannot be same as the Lead,Propietario de Iniciativa no puede ser igual que el de la Iniciativa,
@@ -1535,7 +1535,7 @@ Mark Attendance,Marcar Asistencia,
Mark Half Day,Marcar medio día,
Mark Present,Marcar Presente,
Marketing,Márketing,
-Marketing Expenses,GASTOS DE PUBLICIDAD,
+Marketing Expenses,Gastos de Publicidad,
Marketplace,Mercado,
Marketplace Error,Error de Marketplace,
Masters,Maestros,
@@ -1867,7 +1867,7 @@ Parents Teacher Meeting Attendance,Padres Maestros Asistencia a la Reunión,
Part-time,Tiempo parcial,
Partially Depreciated,Despreciables Parcialmente,
Partially Received,Parcialmente recibido,
-Party,Partido,
+Party,Tercero,
Party Name,Nombre de Parte,
Party Type,Tipo de entidad,
Party Type and Party is mandatory for {0} account,Tipo de Tercero y Tercero es obligatorio para la Cuenta {0},
@@ -2027,7 +2027,7 @@ Please select Company first,"Por favor, seleccione primero la compañía",
Please select Completion Date for Completed Asset Maintenance Log,Seleccione Fecha de Finalización para el Registro de Mantenimiento de Activos Completado,
Please select Completion Date for Completed Repair,Seleccione Fecha de Finalización para la Reparación Completa,
Please select Course,Por favor seleccione Curso,
-Please select Drug,Seleccione Droga,
+Please select Drug,Por favor seleccione fármaco,
Please select Employee,Por favor selecciona Empleado,
Please select Existing Company for creating Chart of Accounts,"Por favor, seleccione empresa ya existente para la creación del plan de cuentas",
Please select Healthcare Service,Por favor seleccione Servicio de Salud,
@@ -2118,7 +2118,7 @@ Please specify from/to range,"Por favor, especifique el rango (desde / hasta)",
Please supply the specified items at the best possible rates,Por favor suministrar los elementos especificados en las mejores tasas posibles,
Please update your status for this training event,Actualice su estado para este evento de capacitación.,
Please wait 3 days before resending the reminder.,Espere 3 días antes de volver a enviar el recordatorio.,
-Point of Sale,Punto de venta,
+Point of Sale,Punto de Venta,
Point-of-Sale,Punto de Venta (POS),
Point-of-Sale Profile,Perfiles de punto de venta (POS),
Portal,Portal,
@@ -2627,7 +2627,7 @@ Sell,Vender,
Selling,Ventas,
Selling Amount,Cantidad de venta,
Selling Price List,Lista de precios de venta,
-Selling Rate,Tasa de ventas,
+Selling Rate,Precio de venta,
"Selling must be checked, if Applicable For is selected as {0}","'Ventas' debe ser seleccionada, si la opción: 'Aplicable para' esta seleccionado como {0}",
Send Grant Review Email,Enviar correo electrónico de revisión de subvención,
Send Now,Enviar ahora,
@@ -2700,9 +2700,9 @@ Setup cheque dimensions for printing,Configurar dimensiones de cheque para la im
Setup default values for POS Invoices,Configurar los valores predeterminados para facturas de POS,
Setup mode of POS (Online / Offline),Modo de configuración de POS (Online / Offline),
Setup your Institute in ERPNext,Configura tu instituto en ERPNext,
-Share Balance,Compartir Saldo,
+Share Balance,Balance de Acciones,
Share Ledger,Share Ledger,
-Share Management,Gestión de Compartir,
+Share Management,Administración de Acciones,
Share Transfer,Transferir Acciones,
Share Type,Tipo de acción,
Shareholder,Accionista,
@@ -3302,7 +3302,7 @@ Where manufacturing operations are carried.,Dónde se realizan las operaciones d
White,Blanco,
Wire Transfer,Transferencia bancaria,
WooCommerce Products,Productos WooCommerce,
-Work In Progress,Trabajo en progreso,
+Work In Progress,Trabajo en Proceso,
Work Order,Orden de trabajo,
Work Order already created for all items with BOM,Órden de Trabajo ya creada para todos los artículos con lista de materiales,
Work Order cannot be raised against a Item Template,La Órden de Trabajo no puede levantarse contra una Plantilla de Artículo,
@@ -3582,7 +3582,7 @@ Accounting Masters,Maestros Contables,
Accounting Period overlaps with {0},El período contable se superpone con {0},
Activity,Actividad,
Add / Manage Email Accounts.,Añadir / Administrar cuentas de correo electrónico.,
-Add Child,Agregar niño,
+Add Child,Agregar hijo,
Add Loan Security,Agregar seguridad de préstamo,
Add Multiple,Añadir Multiple,
Add Participants,Agregar Participantes,
@@ -3706,7 +3706,7 @@ Description,Descripción,
Designation,Puesto,
Difference Value,Valor de diferencia,
Dimension Filter,Filtro de dimensiones,
-Disabled,Discapacitado,
+Disabled,Deshabilitado,
Disbursement and Repayment,Desembolso y reembolso,
Distance cannot be greater than 4000 kms,La distancia no puede ser mayor a 4000 kms,
Do you want to submit the material request,¿Quieres enviar la solicitud de material?,
@@ -4193,7 +4193,7 @@ Total Income This Year,Ingresos totales este año,
Barcode,Código de barras,
Bold,Negrita,
Center,Centro,
-Clear,Claro,
+Clear,Quitar,
Comment,Comentario,
Comments,Comentarios,
DocType,DocType,
@@ -4238,7 +4238,6 @@ Download as JSON,Descargar como JSON,
End date can not be less than start date,la fecha final no puede ser inferior a fecha de Inicio,
For Default Supplier (Optional),Para el proveedor predeterminado (opcional),
From date cannot be greater than To date,La fecha 'Desde' no puede ser mayor que la fecha 'Hasta',
-Get items from,Obtener artículos de,
Group by,Agrupar por,
In stock,En stock,
Item name,Nombre del producto,
@@ -4305,7 +4304,7 @@ Assets not created for {0}. You will have to create asset manually.,Activos no c
Invalid Account,Cuenta no válida,
Purchase Order Required,Orden de compra requerida,
Purchase Receipt Required,Recibo de compra requerido,
-Account Missing,Falta la cuenta,
+Account Missing,Cuenta Faltante,
Requested,Solicitado,
Partially Paid,Parcialmente pagado,
Invalid Account Currency,Moneda de la cuenta no válida,
@@ -5600,7 +5599,7 @@ Call Log,Registro de llamadas,
Received By,Recibido por,
Caller Information,Información de la llamada,
Contact Name,Nombre de contacto,
-Lead ,Dirigir,
+Lead ,Iniciativa,
Lead Name,Nombre de la iniciativa,
Ringing,Zumbido,
Missed,Perdido,
@@ -6619,7 +6618,7 @@ Employee External Work History,Historial de de trabajos anteriores,
Total Experience,Experiencia total,
Default Leave Policy,Política de Licencia Predeterminada,
Default Salary Structure,Estructura de Salario Predeterminada,
-Employee Group Table,Mesa de grupo de empleados,
+Employee Group Table,Tabla de grupo de empleados,
ERPNext User ID,ERP ID de usuario siguiente,
Employee Health Insurance,Seguro de Salud para Empleados,
Health Insurance Name,Nombre del Seguro de Salud,
@@ -6838,10 +6837,10 @@ Employees,Empleados,
Number Of Employees,Número de Empleados,
Employee Details,Detalles del Empleado,
Validate Attendance,Validar la Asistencia,
-Salary Slip Based on Timesheet,Nomina basada en el Parte de Horas,
+Salary Slip Based on Timesheet,Nomina basada horas,
Select Payroll Period,Seleccione el Período de Nómina,
Deduct Tax For Unclaimed Employee Benefits,Deducir Impuestos para beneficios de Empleados no Reclamados,
-Deduct Tax For Unsubmitted Tax Exemption Proof,Deducir impuestos por prueba de exención de impuestos sin enviar,
+Deduct Tax For Unsubmitted Tax Exemption Proof,Deducir impuestos por soporte de exención de impuestos sin enviar,
Select Payment Account to make Bank Entry,Seleccionar la cuenta de pago para hacer la entrada del Banco,
Salary Slips Created,Salario Slips creado,
Salary Slips Submitted,Nómina Salarial Validada,
@@ -7093,7 +7092,7 @@ Total Amount Paid,Cantidad Total Pagada,
Loan Manager,Gerente de préstamos,
Loan Info,Información del Préstamo,
Rate of Interest,Tasa de interés,
-Proposed Pledges,Promesas Propuestas,
+Proposed Pledges,Prendas Propuestas,
Maximum Loan Amount,Cantidad máxima del préstamo,
Repayment Info,Información de la Devolución,
Total Payable Interest,Interés Total a Pagar,
@@ -7340,7 +7339,7 @@ Available Qty at Source Warehouse,Cantidad Disponible en Almacén Fuente,
Available Qty at WIP Warehouse,Cantidad Disponible en Almacén WIP,
Work Order Operation,Operación de Órden de Trabajo,
Operation Description,Descripción de la operación,
-Operation completed for how many finished goods?,Se completo la operación para la cantidad de productos terminados?,
+Operation completed for how many finished goods?,¿Operación completada para cuántos productos terminados?,
Work in Progress,Trabajo en proceso,
Estimated Time and Cost,Tiempo estimado y costo,
Planned Start Time,Hora prevista de inicio,
@@ -8452,7 +8451,7 @@ Asset Depreciation Ledger,Libro Mayor Depreciacion de Activos,
Asset Depreciations and Balances,Depreciaciones de Activos y Saldos,
Available Stock for Packing Items,Inventario Disponible de Artículos de Embalaje,
Bank Clearance Summary,Resumen de Cambios Bancarios,
-Bank Remittance,Remesa bancaria,
+Bank Remittance,Giro Bancario,
Batch Item Expiry Status,Estado de Caducidad de Lote de Productos,
Batch-Wise Balance History,Historial de Saldo por Lotes,
BOM Explorer,BOM Explorer,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Tendencias de ordenes de compra,
Purchase Receipt Trends,Tendencias de recibos de compra,
Purchase Register,Registro de compras,
Quotation Trends,Tendencias de Presupuestos,
-Quoted Item Comparison,Comparación de artículos de Cotización,
Received Items To Be Billed,Recepciones por facturar,
Qty to Order,Cantidad a Solicitar,
Requested Items To Be Transferred,Artículos solicitados para ser transferidos,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,El artículo al que hace re
Therapy Session overlaps with {0},La sesión de terapia se superpone con {0},
Therapy Sessions Overlapping,Superposición de sesiones de terapia,
Therapy Plans,Planes de terapia,
+"Item Code, warehouse, quantity are required on row {0}","El código de artículo, el almacén y la cantidad son obligatorios en la fila {0}",
+Get Items from Material Requests against this Supplier,Obtener artículos de solicitudes de material contra este proveedor,
+Enable European Access,Habilitar el acceso europeo,
+Creating Purchase Order ...,Creando orden de compra ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Seleccione un proveedor de los proveedores predeterminados de los artículos a continuación. En la selección, se realizará una orden de compra contra los artículos que pertenecen al proveedor seleccionado únicamente.",
+Row #{}: You must select {} serial numbers for item {}.,Fila # {}: debe seleccionar {} números de serie para el artículo {}.,
diff --git a/erpnext/translations/es_ar.csv b/erpnext/translations/es_ar.csv
index 9bdcba1195..1fd87236f1 100644
--- a/erpnext/translations/es_ar.csv
+++ b/erpnext/translations/es_ar.csv
@@ -1,5 +1,4 @@
Employee {0} on Half day on {1},"Empleado {0}, media jornada el día {1}",
-Item,Producto,
Communication,Comunicacion,
Components,Componentes,
Cheque Size,Tamaño de Cheque,
diff --git a/erpnext/translations/es_gt.csv b/erpnext/translations/es_gt.csv
index efd2daaa31..29ce2b8a87 100644
--- a/erpnext/translations/es_gt.csv
+++ b/erpnext/translations/es_gt.csv
@@ -1,4 +1,3 @@
-Item,Producto,
Lead Time Days,Tiempo de ejecución en días,
Outstanding,Pendiente,
Outstanding Amount,Saldo Pendiente,
diff --git a/erpnext/translations/et.csv b/erpnext/translations/et.csv
index b778eff4d6..ba32187c68 100644
--- a/erpnext/translations/et.csv
+++ b/erpnext/translations/et.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Laadige alla kui Json,
End date can not be less than start date,End Date saa olla väiksem kui alguskuupäev,
For Default Supplier (Optional),Vaikimisi tarnija (valikuline),
From date cannot be greater than To date,Siit kuupäev ei saa olla suurem kui kuupäev,
-Get items from,Võta esemed,
Group by,Group By,
In stock,Laos,
Item name,Toote nimi,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Ostutellimuse Trends,
Purchase Receipt Trends,Ostutšekk Trends,
Purchase Register,Ostu Registreeri,
Quotation Trends,Tsitaat Trends,
-Quoted Item Comparison,Tsiteeritud Punkt võrdlus,
Received Items To Be Billed,Saadud objekte arve,
Qty to Order,Kogus tellida,
Requested Items To Be Transferred,Taotletud üleantavate,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Üksus, millele viitab {0}
Therapy Session overlaps with {0},Teraapiaseanss kattub rakendusega {0},
Therapy Sessions Overlapping,Teraapiaseansid kattuvad,
Therapy Plans,Teraapiakavad,
+"Item Code, warehouse, quantity are required on row {0}","Real {0} on nõutav kaubakood, ladu ja kogus",
+Get Items from Material Requests against this Supplier,Hankige esemeid selle tarnija vastu esitatud materjalitaotlustest,
+Enable European Access,Luba Euroopa juurdepääs,
+Creating Purchase Order ...,Ostutellimuse loomine ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Valige allpool olevate üksuste vaiketarnijatest tarnija. Valiku alusel tehakse ostutellimus ainult valitud tarnijale kuuluvate kaupade kohta.,
+Row #{}: You must select {} serial numbers for item {}.,Rida nr {}: peate valima {} üksuse seerianumbrid {}.,
diff --git a/erpnext/translations/fa.csv b/erpnext/translations/fa.csv
index 0e4d19a01b..4a7c979499 100644
--- a/erpnext/translations/fa.csv
+++ b/erpnext/translations/fa.csv
@@ -4238,7 +4238,6 @@ Download as JSON,به عنوان JSON بارگیری کنید,
End date can not be less than start date,تاریخ پایان نمی تواند کمتر از تاریخ شروع,
For Default Supplier (Optional),برای تامین کننده پیش فرض (اختیاری),
From date cannot be greater than To date,از تاریخ نمی تواند بیشتر از تاریخ باشد,
-Get items from,گرفتن اقلام از,
Group by,گروه توسط,
In stock,در انبار,
Item name,نام آیتم,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,خرید سفارش روند,
Purchase Receipt Trends,روند رسید خرید,
Purchase Register,خرید ثبت نام,
Quotation Trends,روند نقل قول,
-Quoted Item Comparison,مورد نقل مقایسه,
Received Items To Be Billed,دریافت گزینه هایی که صورتحساب,
Qty to Order,تعداد سفارش,
Requested Items To Be Transferred,آیتم ها درخواست می شود منتقل,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,مورد ارجاع شده
Therapy Session overlaps with {0},جلسه درمانی با {0} همپوشانی دارد,
Therapy Sessions Overlapping,جلسات درمانی با هم تداخل دارند,
Therapy Plans,برنامه های درمانی,
+"Item Code, warehouse, quantity are required on row {0}",کد مورد ، انبار ، مقدار در ردیف {0} لازم است,
+Get Items from Material Requests against this Supplier,مواردی را از درخواستهای ماده در برابر این تأمین کننده دریافت کنید,
+Enable European Access,دسترسی اروپا را فعال کنید,
+Creating Purchase Order ...,در حال ایجاد سفارش خرید ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",از تامین کنندگان پیش فرض موارد زیر یک تأمین کننده انتخاب کنید. هنگام انتخاب ، سفارش خرید فقط در مورد کالاهای متعلق به تنها تامین کننده انتخاب شده انجام می شود.,
+Row #{}: You must select {} serial numbers for item {}.,ردیف شماره {}: شما باید {} شماره سریال را برای مورد {} انتخاب کنید.,
diff --git a/erpnext/translations/fi.csv b/erpnext/translations/fi.csv
index 23fd7a7276..29eb56702d 100644
--- a/erpnext/translations/fi.csv
+++ b/erpnext/translations/fi.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Lataa nimellä JSON,
End date can not be less than start date,päättymispäivä ei voi olla ennen aloituspäivää,
For Default Supplier (Optional),Oletuksena toimittaja (valinnainen),
From date cannot be greater than To date,Alkaen päivä ei voi olla suurempi kuin päättymispäivä,
-Get items from,Hae nimikkeet,
Group by,ryhmän,
In stock,Varastossa,
Item name,Nimikkeen nimi,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Ostotilausten kehitys,
Purchase Receipt Trends,Saapumisten kehitys,
Purchase Register,Osto Rekisteröidy,
Quotation Trends,Tarjousten kehitys,
-Quoted Item Comparison,Noteeratut Kohta Vertailu,
Received Items To Be Billed,Saivat kohteet laskuttamat,
Qty to Order,Tilattava yksikkömäärä,
Requested Items To Be Transferred,siirrettävät pyydetyt tuotteet,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Kohde, johon {0} - {1} vii
Therapy Session overlaps with {0},Hoitoistunto on päällekkäinen kohteen {0} kanssa,
Therapy Sessions Overlapping,Hoitoistunnot ovat päällekkäisiä,
Therapy Plans,Hoitosuunnitelmat,
+"Item Code, warehouse, quantity are required on row {0}","Tuotekoodi, varasto, määrä vaaditaan rivillä {0}",
+Get Items from Material Requests against this Supplier,Hanki tuotteita tältä toimittajalta saaduista materiaalipyynnöistä,
+Enable European Access,Ota käyttöön eurooppalainen käyttöoikeus,
+Creating Purchase Order ...,Luodaan ostotilausta ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Valitse toimittaja alla olevien tuotteiden oletustoimittajista. Valinnan yhteydessä ostotilaus tehdään vain valitulle toimittajalle kuuluvista tuotteista.,
+Row #{}: You must select {} serial numbers for item {}.,Rivi # {}: Sinun on valittava {} tuotteen sarjanumerot {}.,
diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv
index 09a90331c7..3cdae454ab 100644
--- a/erpnext/translations/fr.csv
+++ b/erpnext/translations/fr.csv
@@ -583,7 +583,7 @@ Condition,Conditions,
Configure,Configurer,
Configure {0},Configurer {0},
Confirmed orders from Customers.,Commandes confirmées des clients.,
-Connect Amazon with ERPNext,Connectez Amazon avec ERPNext,
+Connect Amazon with ERPNext,Connecter Amazon avec ERPNext,
Connect Shopify with ERPNext,Connectez Shopify avec ERPNext,
Connect to Quickbooks,Se connecter à Quickbooks,
Connected to QuickBooks,Connecté à QuickBooks,
@@ -4238,7 +4238,6 @@ Download as JSON,Télécharger en JSON,
End date can not be less than start date,La date de Fin ne peut pas être antérieure à la Date de Début,
For Default Supplier (Optional),Pour le fournisseur par défaut (facultatif),
From date cannot be greater than To date,La Date Initiale ne peut pas être postérieure à la Date Finale,
-Get items from,Obtenir les articles de,
Group by,Grouper Par,
In stock,En stock,
Item name,Nom de l'article,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Tendances des Bons de Commande,
Purchase Receipt Trends,Tendances des Reçus d'Achats,
Purchase Register,Registre des Achats,
Quotation Trends,Tendances des Devis,
-Quoted Item Comparison,Comparaison d'Article Soumis,
Received Items To Be Billed,Articles Reçus à Facturer,
Qty to Order,Quantité à Commander,
Requested Items To Be Transferred,Articles Demandés à Transférer,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,L'article référencé
Therapy Session overlaps with {0},La session de thérapie chevauche {0},
Therapy Sessions Overlapping,Chevauchement des séances de thérapie,
Therapy Plans,Plans de thérapie,
+"Item Code, warehouse, quantity are required on row {0}","Le code article, l'entrepôt et la quantité sont obligatoires sur la ligne {0}",
+Get Items from Material Requests against this Supplier,Obtenir des articles à partir de demandes d'articles auprès de ce fournisseur,
+Enable European Access,Activer l'accès européen,
+Creating Purchase Order ...,Création d'une commande d'achat ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Sélectionnez un fournisseur parmi les fournisseurs par défaut des articles ci-dessous. Lors de la sélection, un bon de commande sera effectué contre des articles appartenant uniquement au fournisseur sélectionné.",
+Row #{}: You must select {} serial numbers for item {}.,Ligne n ° {}: vous devez sélectionner {} numéros de série pour l'article {}.,
diff --git a/erpnext/translations/gu.csv b/erpnext/translations/gu.csv
index e25c6bba4a..5c2b520d9e 100644
--- a/erpnext/translations/gu.csv
+++ b/erpnext/translations/gu.csv
@@ -4238,7 +4238,6 @@ Download as JSON,જેસન તરીકે ડાઉનલોડ કરો,
End date can not be less than start date,સમાપ્તિ તારીખ પ્રારંભ તારીખ કરતાં ઓછી હોઈ શકતી નથી,
For Default Supplier (Optional),ડિફોલ્ટ સપ્લાયર માટે (વૈકલ્પિક),
From date cannot be greater than To date,તારીખથી તારીખ કરતાં વધુ હોઈ શકતી નથી,
-Get items from,વસ્તુઓ મેળવો,
Group by,ગ્રુપ દ્વારા,
In stock,ઉપલબ્ધ છે,
Item name,વસ્તુ નામ,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,ઓર્ડર પ્રવાહો ખરીદી,
Purchase Receipt Trends,ખરીદી રસીદ પ્રવાહો,
Purchase Register,ખરીદી રજીસ્ટર,
Quotation Trends,અવતરણ પ્રવાહો,
-Quoted Item Comparison,નોંધાયેલા વસ્તુ સરખામણી,
Received Items To Be Billed,પ્રાપ્ત વસ્તુઓ બિલ કરવા,
Qty to Order,ઓર્ડર Qty,
Requested Items To Be Transferred,વિનંતી વસ્તુઓ ટ્રાન્સફર કરી,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1} દ્વાર
Therapy Session overlaps with {0},ઉપચાર સત્ર {0 with સાથે ઓવરલેપ થાય છે,
Therapy Sessions Overlapping,ઉપચાર સત્રો ઓવરલેપિંગ,
Therapy Plans,ઉપચાર યોજનાઓ,
+"Item Code, warehouse, quantity are required on row {0}","આઇટમ કોડ, વેરહાઉસ, જથ્થો પંક્તિ પર આવશ્યક છે {0}",
+Get Items from Material Requests against this Supplier,આ સપ્લાયર સામે સામગ્રી વિનંતીઓમાંથી આઇટમ્સ મેળવો,
+Enable European Access,યુરોપિયન Enableક્સેસને સક્ષમ કરો,
+Creating Purchase Order ...,ખરીદી Orderર્ડર બનાવી રહ્યાં છે ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","નીચેની આઇટમ્સના ડિફaultલ્ટ સપ્લાયર્સમાંથી સપ્લાયર પસંદ કરો. પસંદગી પર, ફક્ત પસંદ કરેલા સપ્લાયરની વસ્તુઓ સામે ખરીદ ઓર્ડર આપવામાં આવશે.",
+Row #{}: You must select {} serial numbers for item {}.,પંક્તિ # {}: તમારે આઇટમ for for માટે સીરીયલ નંબરો પસંદ કરવા આવશ્યક છે.,
diff --git a/erpnext/translations/he.csv b/erpnext/translations/he.csv
index 43ca52cbe1..29e6f6afc3 100644
--- a/erpnext/translations/he.csv
+++ b/erpnext/translations/he.csv
@@ -4238,7 +4238,6 @@ Download as JSON,הורד כ- JSON,
End date can not be less than start date,תאריך סיום לא יכול להיות פחות מתאריך ההתחלה,
For Default Supplier (Optional),לספק ברירת מחדל (אופציונלי),
From date cannot be greater than To date,מתאריך לא יכול להיות גדול יותר מאשר תאריך,
-Get items from,קבל פריטים מ,
Group by,קבוצה על ידי,
In stock,במלאי,
Item name,שם פריט,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,לרכוש מגמות להזמין,
Purchase Receipt Trends,מגמות קבלת רכישה,
Purchase Register,רכישת הרשמה,
Quotation Trends,מגמות ציטוט,
-Quoted Item Comparison,פריט מצוטט השוואה,
Received Items To Be Billed,פריטים שהתקבלו לחיוב,
Qty to Order,כמות להזמנה,
Requested Items To Be Transferred,פריטים מבוקשים שיועברו,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,הפריט אליו הוז
Therapy Session overlaps with {0},מושב טיפולי חופף עם {0},
Therapy Sessions Overlapping,מפגשי טיפול חופפים,
Therapy Plans,תוכניות טיפול,
+"Item Code, warehouse, quantity are required on row {0}","קוד פריט, מחסן, כמות נדרשים בשורה {0}",
+Get Items from Material Requests against this Supplier,קבל פריטים מבקשות חומר נגד ספק זה,
+Enable European Access,אפשר גישה אירופית,
+Creating Purchase Order ...,יוצר הזמנת רכש ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","בחר ספק מבין ספקי ברירת המחדל של הפריטים למטה. בבחירה, הזמנת רכש תתבצע כנגד פריטים השייכים לספק שנבחר בלבד.",
+Row #{}: You must select {} serial numbers for item {}.,שורה מספר {}: עליך לבחור {} מספרים סידוריים לפריט {}.,
diff --git a/erpnext/translations/hi.csv b/erpnext/translations/hi.csv
index c18532b974..c385fc6ef6 100644
--- a/erpnext/translations/hi.csv
+++ b/erpnext/translations/hi.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSON के रूप में डाउनलोड करे
End date can not be less than start date,समाप्ति तिथि आरंभ तिथि से कम नहीं हो सकता,
For Default Supplier (Optional),डिफ़ॉल्ट प्रदायक (वैकल्पिक) के लिए,
From date cannot be greater than To date,दिनांक से दिनांक से अधिक नहीं हो सकता है,
-Get items from,से आइटम प्राप्त,
Group by,समूह द्वारा,
In stock,स्टॉक में,
Item name,मद का नाम,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,आदेश रुझान खरीद,
Purchase Receipt Trends,खरीद रसीद रुझान,
Purchase Register,इन पंजीकृत खरीद,
Quotation Trends,कोटेशन रुझान,
-Quoted Item Comparison,उद्धरित मद तुलना,
Received Items To Be Billed,बिल करने के लिए प्राप्त आइटम,
Qty to Order,मात्रा आदेश को,
Requested Items To Be Transferred,हस्तांतरित करने का अनुरोध आइटम,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1} द्वार
Therapy Session overlaps with {0},थेरेपी सत्र {0} से ओवरलैप होता है,
Therapy Sessions Overlapping,थेरेपी सेशन ओवरलैपिंग,
Therapy Plans,थेरेपी योजनाएं,
+"Item Code, warehouse, quantity are required on row {0}","पंक्ति {0} पर आइटम कोड, गोदाम, मात्रा आवश्यक है",
+Get Items from Material Requests against this Supplier,इस आपूर्तिकर्ता के विरुद्ध सामग्री अनुरोध से आइटम प्राप्त करें,
+Enable European Access,यूरोपीय पहुँच सक्षम करें,
+Creating Purchase Order ...,क्रय आदेश बनाना ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","नीचे दी गई वस्तुओं के डिफ़ॉल्ट आपूर्तिकर्ता से एक आपूर्तिकर्ता का चयन करें। चयन पर, केवल चयनित आपूर्तिकर्ता से संबंधित वस्तुओं के खिलाफ एक खरीद ऑर्डर किया जाएगा।",
+Row #{}: You must select {} serial numbers for item {}.,पंक्ति # {}: आपको आइटम {} के लिए {} सीरियल नंबर का चयन करना होगा।,
diff --git a/erpnext/translations/hr.csv b/erpnext/translations/hr.csv
index aabacdc0af..a544e98868 100644
--- a/erpnext/translations/hr.csv
+++ b/erpnext/translations/hr.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Preuzmi kao Json,
End date can not be less than start date,Datum završetka ne može biti manja od početnog datuma,
For Default Supplier (Optional),Za dobavljača zadano (neobavezno),
From date cannot be greater than To date,Datum ne može biti veći od datuma,
-Get items from,Nabavite stavke iz,
Group by,Grupiranje prema,
In stock,Na lageru,
Item name,Naziv proizvoda,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Trendovi narudžbenica kupnje,
Purchase Receipt Trends,Trend primki,
Purchase Register,Popis nabave,
Quotation Trends,Trend ponuda,
-Quoted Item Comparison,Citirano predmeta za usporedbu,
Received Items To Be Billed,Primljeni Proizvodi se naplaćuje,
Qty to Order,Količina za narudžbu,
Requested Items To Be Transferred,Traženi proizvodi spremni za transfer,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Stavka na koju se poziva {0
Therapy Session overlaps with {0},Sjednica terapije preklapa se s {0},
Therapy Sessions Overlapping,Preklapajuće se terapijske sesije,
Therapy Plans,Planovi terapije,
+"Item Code, warehouse, quantity are required on row {0}","Šifra artikla, skladište, količina potrebni su na retku {0}",
+Get Items from Material Requests against this Supplier,Nabavite stavke iz materijalnih zahtjeva protiv ovog dobavljača,
+Enable European Access,Omogućiti europski pristup,
+Creating Purchase Order ...,Izrada narudžbenice ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Odaberite dobavljača od zadanih dobavljača dolje navedenih stavki. Nakon odabira, narudžbenica će se izvršiti samo za proizvode koji pripadaju odabranom dobavljaču.",
+Row #{}: You must select {} serial numbers for item {}.,Redak {{}: Morate odabrati {} serijske brojeve za stavku {}.,
diff --git a/erpnext/translations/hu.csv b/erpnext/translations/hu.csv
index 09acda372e..29f347ecbc 100644
--- a/erpnext/translations/hu.csv
+++ b/erpnext/translations/hu.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Töltse le JSON néven,
End date can not be less than start date,"A befejezés dátuma nem lehet kevesebb, mint a kezdő dátum",
For Default Supplier (Optional),Az alapértelmezett beszállító számára (opcionális),
From date cannot be greater than To date,"A dátum nem lehet nagyobb, mint a dátum",
-Get items from,Tételeket kér le innen,
Group by,Csoportosítva,
In stock,Raktáron,
Item name,Tétel neve,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Beszerzési megrendelések alakulása,
Purchase Receipt Trends,Beszerzési nyugták alakulása,
Purchase Register,Beszerzési Regisztráció,
Quotation Trends,Árajánlatok alakulása,
-Quoted Item Comparison,Ajánlott tétel összehasonlítás,
Received Items To Be Billed,Számlázandó Beérkezett tételek,
Qty to Order,Mennyiség Rendeléshez,
Requested Items To Be Transferred,Kérte az átvinni kívánt elemeket,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,A (z) {0} - {1} által hiva
Therapy Session overlaps with {0},A terápiás munkamenet átfedésben van a következővel: {0},
Therapy Sessions Overlapping,A terápiás foglalkozások átfedésben vannak,
Therapy Plans,Terápiás tervek,
+"Item Code, warehouse, quantity are required on row {0}","A (z) {0} sorban tételszám, raktár, mennyiség szükséges",
+Get Items from Material Requests against this Supplier,Tételeket szerezhet a szállítóval szembeni anyagi igényekből,
+Enable European Access,Engedélyezze az európai hozzáférést,
+Creating Purchase Order ...,Megrendelés létrehozása ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Válasszon szállítót az alábbi elemek alapértelmezett szállítói közül. A kiválasztáskor csak a kiválasztott szállítóhoz tartozó termékekre készül megrendelés.,
+Row #{}: You must select {} serial numbers for item {}.,#. Sor:} {0} sorszámokat kell kiválasztania.,
diff --git a/erpnext/translations/id.csv b/erpnext/translations/id.csv
index 53122ccb71..7175ad2fa7 100644
--- a/erpnext/translations/id.csv
+++ b/erpnext/translations/id.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Unduh sebagai JSON,
End date can not be less than start date,Tanggal Berakhir tidak boleh kurang dari Tanggal Mulai,
For Default Supplier (Optional),Untuk Pemasok Default (Opsional),
From date cannot be greater than To date,Dari Tanggal tidak dapat lebih besar dari To Date,
-Get items from,Mendapatkan Stok Barang-Stok Barang dari,
Group by,Kelompok Dengan,
In stock,Persediaan,
Item name,Nama Item,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Trend Order Pembelian,
Purchase Receipt Trends,Tren Nota Penerimaan,
Purchase Register,Register Pembelian,
Quotation Trends,Trend Penawaran,
-Quoted Item Comparison,Perbandingan Produk/Barang yang ditawarkan,
Received Items To Be Billed,Produk Diterima Akan Ditagih,
Qty to Order,Kuantitas untuk diorder,
Requested Items To Be Transferred,Permintaan Produk Akan Ditransfer,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Item yang direferensikan ol
Therapy Session overlaps with {0},Sesi Terapi tumpang tindih dengan {0},
Therapy Sessions Overlapping,Sesi Terapi Tumpang Tindih,
Therapy Plans,Rencana Terapi,
+"Item Code, warehouse, quantity are required on row {0}","Kode Barang, gudang, kuantitas diperlukan di baris {0}",
+Get Items from Material Requests against this Supplier,Dapatkan Item dari Permintaan Material terhadap Pemasok ini,
+Enable European Access,Aktifkan Akses Eropa,
+Creating Purchase Order ...,Membuat Pesanan Pembelian ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Pilih Pemasok dari Pemasok Default item di bawah ini. Saat dipilih, Pesanan Pembelian akan dibuat terhadap barang-barang milik Pemasok terpilih saja.",
+Row #{}: You must select {} serial numbers for item {}.,Baris # {}: Anda harus memilih {} nomor seri untuk item {}.,
diff --git a/erpnext/translations/is.csv b/erpnext/translations/is.csv
index 50828baeee..5f56aff3dc 100644
--- a/erpnext/translations/is.csv
+++ b/erpnext/translations/is.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Sæktu sem JSON,
End date can not be less than start date,Lokadagur getur ekki verið minna en upphafsdagur,
For Default Supplier (Optional),Fyrir Sjálfgefið Birgir (valfrjálst),
From date cannot be greater than To date,Frá Dagsetning má ekki vera meiri en Til Dagsetning,
-Get items from,Fá atriði úr,
Group by,Hópa eftir,
In stock,Á lager,
Item name,Item Name,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Purchase Order Trends,
Purchase Receipt Trends,Kvittun Trends,
Purchase Register,kaup Register,
Quotation Trends,Tilvitnun Trends,
-Quoted Item Comparison,Vitnað Item Samanburður,
Received Items To Be Billed,Móttekin Items verður innheimt,
Qty to Order,Magn til að panta,
Requested Items To Be Transferred,Umbeðin Items til að flytja,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Atriðið sem {0} - {1} ví
Therapy Session overlaps with {0},Meðferðarlotan skarast við {0},
Therapy Sessions Overlapping,Meðferðarlotur skarast,
Therapy Plans,Meðferðaráætlanir,
+"Item Code, warehouse, quantity are required on row {0}","Vörukóði, vöruhús, magn er krafist í línu {0}",
+Get Items from Material Requests against this Supplier,Fáðu hluti frá beiðnum um efni gagnvart þessum birgi,
+Enable European Access,Virkja evrópskan aðgang,
+Creating Purchase Order ...,Býr til innkaupapöntun ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Veldu birgja frá sjálfgefnum birgjum hlutanna hér að neðan. Við val verður eingöngu gerð innkaupapöntun á hlutum sem tilheyra völdum birgi.,
+Row #{}: You must select {} serial numbers for item {}.,Röð nr. {}: Þú verður að velja {} raðnúmer fyrir hlut {}.,
diff --git a/erpnext/translations/it.csv b/erpnext/translations/it.csv
index 4da11f1989..3a1d73f344 100644
--- a/erpnext/translations/it.csv
+++ b/erpnext/translations/it.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Scarica come JSON,
End date can not be less than start date,Data di Fine non può essere inferiore a Data di inizio,
For Default Supplier (Optional),Per fornitore predefinito (facoltativo),
From date cannot be greater than To date,Dalla data non può essere maggiore di Alla data,
-Get items from,Ottenere elementi dal,
Group by,Raggruppa per,
In stock,disponibile,
Item name,Nome Articolo,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Acquisto Tendenze Ordine,
Purchase Receipt Trends,Acquisto Tendenze Receipt,
Purchase Register,Registro Acquisti,
Quotation Trends,Tendenze di preventivo,
-Quoted Item Comparison,Articolo Citato Confronto,
Received Items To Be Billed,Oggetti ricevuti da fatturare,
Qty to Order,Qtà da Ordinare,
Requested Items To Be Transferred,Voci si chiede il trasferimento,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,L'articolo a cui fa rif
Therapy Session overlaps with {0},La sessione di terapia si sovrappone a {0},
Therapy Sessions Overlapping,Sessioni di terapia sovrapposte,
Therapy Plans,Piani terapeutici,
+"Item Code, warehouse, quantity are required on row {0}","Codice articolo, magazzino e quantità sono obbligatori nella riga {0}",
+Get Items from Material Requests against this Supplier,Ottieni articoli da richieste di materiale contro questo fornitore,
+Enable European Access,Consentire l'accesso europeo,
+Creating Purchase Order ...,Creazione dell'ordine di acquisto ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Seleziona un fornitore dai fornitori predefiniti degli articoli seguenti. Alla selezione, verrà effettuato un Ordine di acquisto solo per gli articoli appartenenti al Fornitore selezionato.",
+Row #{}: You must select {} serial numbers for item {}.,Riga # {}: è necessario selezionare i {} numeri di serie per l'articolo {}.,
diff --git a/erpnext/translations/ja.csv b/erpnext/translations/ja.csv
index 3fa1c18a48..6e2eaae4a4 100644
--- a/erpnext/translations/ja.csv
+++ b/erpnext/translations/ja.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSONとしてダウンロード,
End date can not be less than start date,終了日は開始日より短くすることはできません,
For Default Supplier (Optional),デフォルトサプライヤ(オプション),
From date cannot be greater than To date,開始日を終了日より大きくすることはできません,
-Get items from,アイテム取得元,
Group by,グループ化,
In stock,在庫あり,
Item name,アイテム名,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,発注傾向,
Purchase Receipt Trends,領収書傾向,
Purchase Register,仕入帳,
Quotation Trends,見積傾向,
-Quoted Item Comparison,引用符で囲まれた項目の比較,
Received Items To Be Billed,支払予定受領アイテム,
Qty to Order,注文数,
Requested Items To Be Transferred,移転予定の要求アイテム,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0}-{1}で参照されて
Therapy Session overlaps with {0},セラピーセッションが{0}と重複しています,
Therapy Sessions Overlapping,重複する治療セッション,
Therapy Plans,治療計画,
+"Item Code, warehouse, quantity are required on row {0}",行{0}にはアイテムコード、倉庫、数量が必要です,
+Get Items from Material Requests against this Supplier,このサプライヤーに対する重要な要求からアイテムを取得する,
+Enable European Access,ヨーロッパへのアクセスを有効にする,
+Creating Purchase Order ...,注文書の作成..。,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",以下の項目のデフォルトサプライヤーからサプライヤーを選択します。選択時に、選択したサプライヤーに属するアイテムに対してのみ発注書が作成されます。,
+Row #{}: You must select {} serial numbers for item {}.,行#{}:アイテム{}の{}シリアル番号を選択する必要があります。,
diff --git a/erpnext/translations/km.csv b/erpnext/translations/km.csv
index e1a01b018c..e2a528cea2 100644
--- a/erpnext/translations/km.csv
+++ b/erpnext/translations/km.csv
@@ -4238,7 +4238,6 @@ Download as JSON,ទាញយកជា Json ។,
End date can not be less than start date,កាលបរិច្ឆេទបញ្ចប់មិនអាចតិចជាងកាលបរិច្ឆេទចាប់ផ្ដើមទេ,
For Default Supplier (Optional),សម្រាប់អ្នកផ្គត់ផ្គង់លំនាំដើម (ស្រេចចិត្ត),
From date cannot be greater than To date,ពីកាលបរិច្ឆេទមិនអាចមានចំនួនច្រើនជាងកាលបរិច្ឆេទ,
-Get items from,ទទួលបានមុខទំនិញពី,
Group by,ក្រុមតាម,
In stock,នៅក្នុងស្តុក,
Item name,ឈ្មោះធាតុ,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,ទិញលំដាប់និន្នាការ,
Purchase Receipt Trends,និន្នាការបង្កាន់ដៃទិញ,
Purchase Register,ទិញចុះឈ្មោះ,
Quotation Trends,សម្រង់និន្នាការ,
-Quoted Item Comparison,ធាតុដកស្រង់សម្តីប្រៀបធៀប,
Received Items To Be Billed,ទទួលបានធាតុដែលនឹងត្រូវបានផ្សព្វផ្សាយ,
Qty to Order,qty ម៉ង់ទិញ,
Requested Items To Be Transferred,ធាតុដែលបានស្នើសុំឱ្យគេបញ្ជូន,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,ធាតុដែលយោ
Therapy Session overlaps with {0},វគ្គនៃការព្យាបាលត្រួតគ្នាជាមួយ {0},
Therapy Sessions Overlapping,ការព្យាបាលដោយការត្រួតគ្នា,
Therapy Plans,ផែនការព្យាបាល,
+"Item Code, warehouse, quantity are required on row {0}",លេខកូដទំនិញឃ្លាំងបរិមាណត្រូវបានទាមទារនៅជួរ {0},
+Get Items from Material Requests against this Supplier,ទទួលបានវត្ថុពីសំណើសម្ភារៈប្រឆាំងនឹងអ្នកផ្គត់ផ្គង់នេះ,
+Enable European Access,បើកដំណើរការចូលអឺរ៉ុប,
+Creating Purchase Order ...,កំពុងបង្កើតការបញ្ជាទិញ ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",ជ្រើសរើសអ្នកផ្គត់ផ្គង់ពីអ្នកផ្គត់ផ្គង់លំនាំដើមនៃធាតុខាងក្រោម។ នៅពេលជ្រើសរើសការបញ្ជាទិញនឹងត្រូវធ្វើឡើងប្រឆាំងនឹងរបស់របរដែលជាកម្មសិទ្ធិរបស់អ្នកផ្គត់ផ្គង់ដែលបានជ្រើសរើសតែប៉ុណ្ណោះ។,
+Row #{}: You must select {} serial numbers for item {}.,ជួរទី # {}៖ អ្នកត្រូវតែជ្រើសរើស {} លេខស៊េរីសម្រាប់ធាតុ {} ។,
diff --git a/erpnext/translations/kn.csv b/erpnext/translations/kn.csv
index fdd798dbfc..4a9173d4a0 100644
--- a/erpnext/translations/kn.csv
+++ b/erpnext/translations/kn.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSON ಆಗಿ ಡೌನ್ಲೋಡ್ ಮಾಡಿ,
End date can not be less than start date,ಅಂತಿಮ ದಿನಾಂಕ ಪ್ರಾರಂಭ ದಿನಾಂಕಕ್ಕಿಂತ ಕಡಿಮೆ ಇರುವಂತಿಲ್ಲ,
For Default Supplier (Optional),ಡೀಫಾಲ್ಟ್ ಪೂರೈಕೆದಾರರಿಗಾಗಿ (ಐಚ್ಛಿಕ),
From date cannot be greater than To date,ದಿನಾಂಕದಿಂದ ದಿನಾಂಕಕ್ಕಿಂತಲೂ ಹೆಚ್ಚಿನದಾಗಿರುವಂತಿಲ್ಲ,
-Get items from,ಐಟಂಗಳನ್ನು ಪಡೆಯಿರಿ,
Group by,ಗುಂಪಿನ,
In stock,ಉಪಲಬ್ದವಿದೆ,
Item name,ಐಟಂ ಹೆಸರು,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,ಆರ್ಡರ್ ಟ್ರೆಂಡ್ಸ್ ಖರಿ
Purchase Receipt Trends,ಖರೀದಿ ರಸೀತಿ ಟ್ರೆಂಡ್ಸ್,
Purchase Register,ಖರೀದಿ ನೋಂದಣಿ,
Quotation Trends,ನುಡಿಮುತ್ತುಗಳು ಟ್ರೆಂಡ್ಸ್,
-Quoted Item Comparison,ಉಲ್ಲೇಖಿಸಿದ ಐಟಂ ಹೋಲಿಕೆ,
Received Items To Be Billed,ಪಾವತಿಸಬೇಕಾಗುತ್ತದೆ ಸ್ವೀಕರಿಸಿದ ಐಟಂಗಳು,
Qty to Order,ಪ್ರಮಾಣ ಆರ್ಡರ್,
Requested Items To Be Transferred,ಬದಲಾಯಿಸಿಕೊಳ್ಳುವಂತೆ ವಿನಂತಿಸಲಾಗಿದೆ ಐಟಂಗಳು,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1 by ನಿಂದ
Therapy Session overlaps with {0},ಥೆರಪಿ ಸೆಷನ್ {0 with ನೊಂದಿಗೆ ಅತಿಕ್ರಮಿಸುತ್ತದೆ,
Therapy Sessions Overlapping,ಥೆರಪಿ ಸೆಷನ್ಗಳು ಅತಿಕ್ರಮಿಸುತ್ತವೆ,
Therapy Plans,ಚಿಕಿತ್ಸೆಯ ಯೋಜನೆಗಳು,
+"Item Code, warehouse, quantity are required on row {0}","Code 0 row ಸಾಲಿನಲ್ಲಿ ಐಟಂ ಕೋಡ್, ಗೋದಾಮು, ಪ್ರಮಾಣ ಅಗತ್ಯವಿದೆ",
+Get Items from Material Requests against this Supplier,ಈ ಸರಬರಾಜುದಾರರ ವಿರುದ್ಧ ವಸ್ತು ವಿನಂತಿಗಳಿಂದ ವಸ್ತುಗಳನ್ನು ಪಡೆಯಿರಿ,
+Enable European Access,ಯುರೋಪಿಯನ್ ಪ್ರವೇಶವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ,
+Creating Purchase Order ...,ಖರೀದಿ ಆದೇಶವನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","ಕೆಳಗಿನ ಐಟಂಗಳ ಡೀಫಾಲ್ಟ್ ಪೂರೈಕೆದಾರರಿಂದ ಸರಬರಾಜುದಾರರನ್ನು ಆಯ್ಕೆಮಾಡಿ. ಆಯ್ಕೆಯ ಮೇಲೆ, ಆಯ್ದ ಸರಬರಾಜುದಾರರಿಗೆ ಮಾತ್ರ ಸೇರಿದ ವಸ್ತುಗಳ ವಿರುದ್ಧ ಖರೀದಿ ಆದೇಶವನ್ನು ಮಾಡಲಾಗುತ್ತದೆ.",
+Row #{}: You must select {} serial numbers for item {}.,ಸಾಲು # {}: ನೀವು item item ಐಟಂಗೆ {} ಸರಣಿ ಸಂಖ್ಯೆಗಳನ್ನು ಆರಿಸಬೇಕು.,
diff --git a/erpnext/translations/ko.csv b/erpnext/translations/ko.csv
index 1ba119cdb9..c051b07bea 100644
--- a/erpnext/translations/ko.csv
+++ b/erpnext/translations/ko.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSON으로 다운로드,
End date can not be less than start date,종료일은 시작일보다 짧을 수 없습니다.,
For Default Supplier (Optional),기본 공급 업체 (선택 사항),
From date cannot be greater than To date,날짜에서 날짜보다 클 수 없습니다,
-Get items from,에서 항목을 가져 오기,
Group by,그룹으로,
In stock,재고,
Item name,품명,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,주문 동향을 구매,
Purchase Receipt Trends,구매 영수증 동향,
Purchase Register,회원에게 구매,
Quotation Trends,견적 동향,
-Quoted Item Comparison,인용 상품 비교,
Received Items To Be Billed,청구에 주어진 항목,
Qty to Order,수량은 주문,
Requested Items To Be Transferred,전송할 요청 항목,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0}-{1}에서 참조하는
Therapy Session overlaps with {0},치료 세션이 {0}와 (과) 겹칩니다.,
Therapy Sessions Overlapping,겹치는 치료 세션,
Therapy Plans,치료 계획,
+"Item Code, warehouse, quantity are required on row {0}","{0} 행에 품목 코드, 창고, 수량이 필요합니다.",
+Get Items from Material Requests against this Supplier,이 공급자에 대한 자재 요청에서 품목 가져 오기,
+Enable European Access,유럽 액세스 활성화,
+Creating Purchase Order ...,구매 오더 생성 ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",아래 항목의 기본 공급자에서 공급자를 선택합니다. 선택시 선택한 공급 업체에 속한 품목에 대해서만 구매 주문이 작성됩니다.,
+Row #{}: You must select {} serial numbers for item {}.,행 # {} : 항목 {}에 대해 {} 일련 번호를 선택해야합니다.,
diff --git a/erpnext/translations/ku.csv b/erpnext/translations/ku.csv
index 5d9d472071..6962ea1ef1 100644
--- a/erpnext/translations/ku.csv
+++ b/erpnext/translations/ku.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Wekî JSON dakêşin,
End date can not be less than start date,Dîrok Dîroka Destpêk Destpêk Dibe,
For Default Supplier (Optional),Ji bo Default Supplier (alternatîf),
From date cannot be greater than To date,Ji Date ne dikarin bibin mezintir To Date,
-Get items from,Get tomar ji,
Group by,Koma By,
In stock,Ez bêzarim,
Item name,Navê Navekî,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Bikirin Order Trends,
Purchase Receipt Trends,Trends kirînê Meqbûz,
Purchase Register,Buy Register,
Quotation Trends,Trends quotation,
-Quoted Item Comparison,Babetê têbinî eyna,
Received Items To Be Billed,Pêşwaziya Nawy ye- Be,
Qty to Order,Qty siparîş,
Requested Items To Be Transferred,Nawy xwestin veguhestin,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Tişta ku ji hêla {0} - {1
Therapy Session overlaps with {0},Danişîna Terapiyê bi {0} re li hevûdu dike,
Therapy Sessions Overlapping,Danişînên Terapiyê Li Hev Dikevin,
Therapy Plans,Planên Terapiyê,
+"Item Code, warehouse, quantity are required on row {0}","Koda tiştê, embarê, hejmar li ser rêzê hewce ne {0}",
+Get Items from Material Requests against this Supplier,Li dijî vê Pêşkêşkerê Tiştan ji Daxwazên Maddî bistînin,
+Enable European Access,Destûra Ewropî çalak bikin,
+Creating Purchase Order ...,Afirandina Biryara Kirînê ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Ji Pargîdaniyên Pêşwext ên jêrîn ve Pêşkêşvanek hilbijêrin. Li ser hilbijartinê, dê Biryarnameyek Kirînê li dijî tiştên ku tenê ji Pêşkêşkarê hilbijartî ne pêk were.",
+Row #{}: You must select {} serial numbers for item {}.,Rêzok # {}: Divê hûn {} hejmarên rêzê ji bo hêmanê {} hilbijêrin.,
diff --git a/erpnext/translations/lo.csv b/erpnext/translations/lo.csv
index 2220ac3c17..b61476cf17 100644
--- a/erpnext/translations/lo.csv
+++ b/erpnext/translations/lo.csv
@@ -4238,7 +4238,6 @@ Download as JSON,ດາວໂຫລດເປັນ JSON,
End date can not be less than start date,ວັນສິ້ນສຸດບໍ່ສາມາດນ້ອຍກວ່າວັນເລີ່ມຕົ້ນ,
For Default Supplier (Optional),ສໍາລັບຜູ້ໃຫ້ບໍລິການມາດຕະຖານ (ທາງເລືອກ),
From date cannot be greater than To date,ຈາກວັນທີບໍ່ສາມາດຈະມີຫຼາຍຂຶ້ນກ່ວາເຖິງວັນທີ່,
-Get items from,ໄດ້ຮັບການລາຍການຈາກ,
Group by,ກຸ່ມໂດຍ,
In stock,ໃນສາງ,
Item name,ຊື່ສິນຄ້າ,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,ຊື້ແນວໂນ້ມຄໍາສັ່ງ,
Purchase Receipt Trends,ແນວໂນ້ມການຊື້ຮັບ,
Purchase Register,ລົງທະບຽນການຊື້,
Quotation Trends,ແນວໂນ້ມວົງຢືມ,
-Quoted Item Comparison,ປຽບທຽບບາຍດີທຸກທ່ານ Item,
Received Items To Be Billed,ລາຍການທີ່ໄດ້ຮັບການໄດ້ຮັບການ billed,
Qty to Order,ຈໍານວນທີ່ຈະສັ່ງຊື້ສິນຄ້າ,
Requested Items To Be Transferred,ການຮ້ອງຂໍໃຫ້ໄດ້ຮັບການໂອນ,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,ລາຍການທີ່
Therapy Session overlaps with {0},Therapy Session ຊ້ ຳ ກັບ {0},
Therapy Sessions Overlapping,ການປິ່ນປົວດ້ວຍການຊໍ້າຊ້ອນ,
Therapy Plans,ແຜນການປິ່ນປົວ,
+"Item Code, warehouse, quantity are required on row {0}","ລະຫັດສິນຄ້າ, ຄັງສິນຄ້າ, ຈຳ ນວນທີ່ຕ້ອງການຢູ່ແຖວ {0}",
+Get Items from Material Requests against this Supplier,ໄດ້ຮັບສິນຄ້າຈາກຂໍ້ຮຽກຮ້ອງດ້ານວັດຖຸຕໍ່ຜູ້ສະ ໜອງ ນີ້,
+Enable European Access,ເປີດໃຊ້ງານ European Access,
+Creating Purchase Order ...,ການສ້າງໃບສັ່ງຊື້ ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","ເລືອກຜູ້ສະ ໜອງ ສິນຄ້າຈາກຜູ້ສະ ໜອງ ສິນຄ້າເລີ່ມຕົ້ນຂອງລາຍການຂ້າງລຸ່ມນີ້. ໃນການເລືອກ, ຄຳ ສັ່ງຊື້ສິນຄ້າຈະຖືກຕໍ່ຕ້ານກັບສິນຄ້າທີ່ເປັນຂອງຜູ້ສະ ໜອງ ທີ່ຖືກຄັດເລືອກເທົ່ານັ້ນ.",
+Row #{}: You must select {} serial numbers for item {}.,ແຖວ # {}: ທ່ານຕ້ອງເລືອກ {} ເລກ ລຳ ດັບ ສຳ ລັບລາຍການ {}.,
diff --git a/erpnext/translations/lt.csv b/erpnext/translations/lt.csv
index e98f2f2143..78571f9624 100644
--- a/erpnext/translations/lt.csv
+++ b/erpnext/translations/lt.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Atsisiųsti kaip JSON,
End date can not be less than start date,Galutinė data negali būti mažesnė už pradžios datą,
For Default Supplier (Optional),Numatytam tiekėjui (neprivaloma),
From date cannot be greater than To date,Nuo datos negali būti didesnis nei iki datos,
-Get items from,Gauk elementus iš,
Group by,Grupuoti pagal,
In stock,Sandelyje,
Item name,Daikto pavadinimas,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Pirkimui užsakyti tendencijos,
Purchase Receipt Trends,Pirkimo kvito tendencijos,
Purchase Register,pirkimo Registruotis,
Quotation Trends,Kainų tendencijos,
-Quoted Item Comparison,Cituojamas punktas Palyginimas,
Received Items To Be Billed,Gauti duomenys turi būti apmokestinama,
Qty to Order,Kiekis užsisakyti,
Requested Items To Be Transferred,Pageidaujami daiktai turi būti perkeltos,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Elementas, nurodytas {0} -
Therapy Session overlaps with {0},Terapijos seansas sutampa su {0},
Therapy Sessions Overlapping,Terapijos seansai sutampa,
Therapy Plans,Terapijos planai,
+"Item Code, warehouse, quantity are required on row {0}","Prekės kodas, sandėlis, kiekis būtini {0} eilutėje",
+Get Items from Material Requests against this Supplier,Gaukite elementus iš šio tiekėjo materialinių užklausų,
+Enable European Access,Įgalinti Europos prieigą,
+Creating Purchase Order ...,Kuriamas pirkimo užsakymas ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Pasirinkite tiekėją iš toliau nurodytų gaminių iš numatytųjų tiekėjų. Pasirinkus pirkimą, bus sudarytos tik prekės, priklausančios pasirinktam tiekėjui.",
+Row #{}: You must select {} serial numbers for item {}.,# Eilutė {}: turite pasirinkti {} prekės serijos numerius {}.,
diff --git a/erpnext/translations/lv.csv b/erpnext/translations/lv.csv
index 5fdedc8fc7..cbf04855d0 100644
--- a/erpnext/translations/lv.csv
+++ b/erpnext/translations/lv.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Lejupielādēt kā JSON,
End date can not be less than start date,Beigu Datums nevar būt mazāks par sākuma datuma,
For Default Supplier (Optional),Paredzētajam piegādātājam (neobligāti),
From date cannot be greater than To date,No datuma nevar būt lielāka par datumu,
-Get items from,Dabūtu preces no,
Group by,Group By,
In stock,Noliktavā,
Item name,Vienības nosaukums,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Pirkuma pasūtījuma tendences,
Purchase Receipt Trends,Pirkuma čeka tendences,
Purchase Register,Pirkuma Reģistrēties,
Quotation Trends,Piedāvājumu tendences,
-Quoted Item Comparison,Citēts Prece salīdzinājums,
Received Items To Be Billed,Saņemtie posteņi ir Jāmaksā,
Qty to Order,Daudz pasūtījuma,
Requested Items To Be Transferred,Pieprasīto pozīcijas jāpārskaita,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Vienumam, uz kuru atsaucas
Therapy Session overlaps with {0},Terapijas sesija pārklājas ar {0},
Therapy Sessions Overlapping,Terapijas sesijas pārklājas,
Therapy Plans,Terapijas plāni,
+"Item Code, warehouse, quantity are required on row {0}","Rindā {0} ir nepieciešams preces kods, noliktava un daudzums",
+Get Items from Material Requests against this Supplier,Iegūstiet preces no materiāliem pieprasījumiem pret šo piegādātāju,
+Enable European Access,Iespējot Eiropas piekļuvi,
+Creating Purchase Order ...,Notiek pirkuma pasūtījuma izveide ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Izvēlieties piegādātāju no tālāk norādīto vienumu noklusējuma piegādātājiem. Pēc izvēles tiks veikts pirkuma pasūtījums tikai par precēm, kas pieder izvēlētajam piegādātājam.",
+Row #{}: You must select {} serial numbers for item {}.,#. Rinda: jums jāizvēlas {} vienuma sērijas numuri {}.,
diff --git a/erpnext/translations/mk.csv b/erpnext/translations/mk.csv
index fa3de63675..7008025534 100644
--- a/erpnext/translations/mk.csv
+++ b/erpnext/translations/mk.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Преземете како JSON,
End date can not be less than start date,Крајниот датум не може да биде помал од датумот на почеток,
For Default Supplier (Optional),За стандарден добавувач (опционално),
From date cannot be greater than To date,Од датумот не може да биде поголем од датум,
-Get items from,Се предмети од,
Group by,Со група,
In stock,На залиха,
Item name,Точка Име,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Нарачка трендови,
Purchase Receipt Trends,Купување Потврда трендови,
Purchase Register,Купување Регистрирај се,
Quotation Trends,Трендови на Понуди,
-Quoted Item Comparison,Цитирано Точка споредба,
Received Items To Be Billed,Примените предмети да бидат фактурирани,
Qty to Order,Количина да нарачате,
Requested Items To Be Transferred,Бара предмети да бидат префрлени,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Ставката на ко
Therapy Session overlaps with {0},Сесијата за терапија се преклопува со {0},
Therapy Sessions Overlapping,Сесии за терапија што се преклопуваат,
Therapy Plans,Планови за терапија,
+"Item Code, warehouse, quantity are required on row {0}","Кодот на објектот, складиштето, количината се потребни на редот {0}",
+Get Items from Material Requests against this Supplier,Набавете предмети од материјални барања против овој добавувач,
+Enable European Access,Овозможете европски пристап,
+Creating Purchase Order ...,Создавање налог за набавка ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Изберете снабдувач од Стандардните добавувачи на ставките подолу. При избор, ќе се изврши Нарачка за набавка само на предмети што припаѓаат на избраниот Добавувач.",
+Row #{}: You must select {} serial numbers for item {}.,Ред # {}: Мора да изберете {} сериски броеви за ставка {}.,
diff --git a/erpnext/translations/ml.csv b/erpnext/translations/ml.csv
index 6c9bcfc655..f917969345 100644
--- a/erpnext/translations/ml.csv
+++ b/erpnext/translations/ml.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSON ആയി ഡൗൺലോഡുചെയ്യുക,
End date can not be less than start date,അവസാന തീയതി ആരംഭ തീയതിക്ക് കുറവായിരിക്കരുത്,
For Default Supplier (Optional),സ്ഥിര വിതരണക്കാരന് (ഓപ്ഷണൽ),
From date cannot be greater than To date,തീയതി മുതൽ തീയതി വരെ വലിയതായിരിക്കരുത്,
-Get items from,നിന്ന് ഇനങ്ങൾ നേടുക,
Group by,ഗ്രൂപ്പ്,
In stock,സ്റ്റോക്കുണ്ട്,
Item name,ഇനം പേര്,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,ഓർഡർ ട്രെൻഡുകൾ വാങ്ങ
Purchase Receipt Trends,വാങ്ങൽ രസീത് ട്രെൻഡുകൾ,
Purchase Register,രജിസ്റ്റർ വാങ്ങുക,
Quotation Trends,ക്വട്ടേഷൻ ട്രെൻഡുകൾ,
-Quoted Item Comparison,ഉദ്ധരിച്ച ഇനം താരതമ്യം,
Received Items To Be Billed,ബില്ല് ലഭിച്ച ഇനങ്ങൾ,
Qty to Order,ഓർഡർ Qty,
Requested Items To Be Transferred,മാറ്റിയത് അഭ്യർത്ഥിച്ചു ഇനങ്ങൾ,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1 by പരാമർ
Therapy Session overlaps with {0},തെറാപ്പി സെഷൻ {0 with ഓവർലാപ്പ് ചെയ്യുന്നു,
Therapy Sessions Overlapping,തെറാപ്പി സെഷനുകൾ ഓവർലാപ്പുചെയ്യുന്നു,
Therapy Plans,തെറാപ്പി പദ്ധതികൾ,
+"Item Code, warehouse, quantity are required on row {0}","Code 0 row വരിയിൽ ഇനം കോഡ്, വെയർഹ house സ്, അളവ് ആവശ്യമാണ്",
+Get Items from Material Requests against this Supplier,ഈ വിതരണക്കാരനെതിരായ മെറ്റീരിയൽ അഭ്യർത്ഥനകളിൽ നിന്ന് ഇനങ്ങൾ നേടുക,
+Enable European Access,യൂറോപ്യൻ ആക്സസ് പ്രാപ്തമാക്കുക,
+Creating Purchase Order ...,വാങ്ങൽ ഓർഡർ സൃഷ്ടിക്കുന്നു ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","ചുവടെയുള്ള ഇനങ്ങളുടെ സ്ഥിരസ്ഥിതി വിതരണക്കാരിൽ നിന്ന് ഒരു വിതരണക്കാരനെ തിരഞ്ഞെടുക്കുക. തിരഞ്ഞെടുക്കുമ്പോൾ, തിരഞ്ഞെടുത്ത വിതരണക്കാരന്റെ മാത്രം ഇനങ്ങൾക്കെതിരെ ഒരു വാങ്ങൽ ഓർഡർ നൽകും.",
+Row #{}: You must select {} serial numbers for item {}.,വരി # {}: നിങ്ങൾ item item ഇനത്തിനായി {} സീരിയൽ നമ്പറുകൾ തിരഞ്ഞെടുക്കണം.,
diff --git a/erpnext/translations/mr.csv b/erpnext/translations/mr.csv
index 1d63caf508..9c41ce6f33 100644
--- a/erpnext/translations/mr.csv
+++ b/erpnext/translations/mr.csv
@@ -4238,7 +4238,6 @@ Download as JSON,जेसन म्हणून डाउनलोड करा
End date can not be less than start date,समाप्ती तारीख प्रारंभ तारखेच्या पेक्षा कमी असू शकत नाही,
For Default Supplier (Optional),डीफॉल्ट सप्लायर (वैकल्पिक) साठी,
From date cannot be greater than To date,तारखेपासून तारखेपेक्षा जास्त असू शकत नाही,
-Get items from,आयटम मिळवा,
Group by,गट,
In stock,स्टॉक मध्ये,
Item name,आयटम नाव,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,ऑर्डर ट्रेन्ड खरेदी,
Purchase Receipt Trends,खरेदी पावती ट्रेन्ड,
Purchase Register,खरेदी नोंदणी,
Quotation Trends,कोटेशन ट्रेन्ड,
-Quoted Item Comparison,उद्धृत बाबींचा तुलना,
Received Items To Be Billed,बिल करायचे प्राप्त आयटम,
Qty to Order,मागणी करण्यासाठी Qty,
Requested Items To Be Transferred,विनंती आयटम हस्तांतरित करणे,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1} द्वार
Therapy Session overlaps with {0},थेरपी सत्र {0 with सह आच्छादित होते,
Therapy Sessions Overlapping,थेरपी सत्रे आच्छादित,
Therapy Plans,थेरपी योजना,
+"Item Code, warehouse, quantity are required on row {0}","पंक्तीवर आयटम कोड, कोठार, प्रमाण आवश्यक आहे {0}",
+Get Items from Material Requests against this Supplier,या पुरवठादाराविरूद्ध मटेरियल रिक्वेस्टचे आयटम मिळवा,
+Enable European Access,युरोपियन प्रवेश सक्षम करा,
+Creating Purchase Order ...,खरेदी ऑर्डर तयार करत आहे ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","खालील बाबींच्या डीफॉल्ट पुरवठादाराकडील पुरवठादार निवडा. निवडीवर, केवळ निवडलेल्या पुरवठादाराच्या वस्तूंच्या विरुद्ध खरेदी ऑर्डर देण्यात येईल.",
+Row #{}: You must select {} serial numbers for item {}.,पंक्ती # {}: आपण आयटम for for साठी} numbers अनुक्रमांक निवडणे आवश्यक आहे.,
diff --git a/erpnext/translations/ms.csv b/erpnext/translations/ms.csv
index 1d9aa75987..1483844990 100644
--- a/erpnext/translations/ms.csv
+++ b/erpnext/translations/ms.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Muat turun sebagai JSON,
End date can not be less than start date,Tarikh akhir tidak boleh kurang daripada tarikh mula,
For Default Supplier (Optional),Untuk pembekal lalai (pilihan),
From date cannot be greater than To date,Dari Tarikh tidak boleh lebih besar daripada Dating,
-Get items from,Mendapatkan barangan dari,
Group by,Group By,
In stock,Dalam stok,
Item name,Nama Item,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Membeli Trend Pesanan,
Purchase Receipt Trends,Trend Resit Pembelian,
Purchase Register,Pembelian Daftar,
Quotation Trends,Trend Sebut Harga,
-Quoted Item Comparison,Perkara dipetik Perbandingan,
Received Items To Be Billed,Barangan yang diterima dikenakan caj,
Qty to Order,Qty Aturan,
Requested Items To Be Transferred,Item yang diminta Akan Dipindahkan,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Item yang dirujuk oleh {0}
Therapy Session overlaps with {0},Sesi Terapi bertindih dengan {0},
Therapy Sessions Overlapping,Sesi Terapi Bertindih,
Therapy Plans,Rancangan Terapi,
+"Item Code, warehouse, quantity are required on row {0}","Kod Item, gudang, kuantiti diperlukan pada baris {0}",
+Get Items from Material Requests against this Supplier,Dapatkan Item dari Permintaan Bahan terhadap Pembekal ini,
+Enable European Access,Dayakan Akses Eropah,
+Creating Purchase Order ...,Membuat Pesanan Pembelian ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Pilih Pembekal dari Pembekal Lalai item di bawah. Pada pilihan, Pesanan Pembelian akan dibuat terhadap barang-barang milik Pembekal terpilih sahaja.",
+Row #{}: You must select {} serial numbers for item {}.,Baris # {}: Anda mesti memilih {} nombor siri untuk item {}.,
diff --git a/erpnext/translations/my.csv b/erpnext/translations/my.csv
index a505a5ccb5..d15ec1ec71 100644
--- a/erpnext/translations/my.csv
+++ b/erpnext/translations/my.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSON အဖြစ် Download,
End date can not be less than start date,အဆုံးနေ့စွဲ Start ကိုနေ့စွဲထက်လျော့နည်းမဖွစျနိုငျ,
For Default Supplier (Optional),ပုံမှန်ပေးသွင်း (ရွေးချယ်နိုင်),
From date cannot be greater than To date,နေ့စွဲကနေနေ့စွဲရန်ထက် သာ. ကြီးမြတ်မဖွစျနိုငျ,
-Get items from,အထဲကပစ္စည်းတွေကို Get,
Group by,Group မှဖြင့်,
In stock,ကုန်ပစ္စည်းလက်ဝယ်ရှိ,
Item name,item အမည်,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,အမိန့်ခေတ်ရေစီးကြော
Purchase Receipt Trends,ဝယ်ယူခြင်းပြေစာခေတ်ရေစီးကြောင်း,
Purchase Register,မှတ်ပုံတင်မည်ဝယ်ယူ,
Quotation Trends,စျေးနှုန်းခေတ်ရေစီးကြောင်း,
-Quoted Item Comparison,ကိုးကားအရာဝတ္ထုနှိုင်းယှဉ်ခြင်း,
Received Items To Be Billed,ကြေညာတဲ့ခံရဖို့ရရှိထားသည့်ပစ္စည်းများ,
Qty to Order,ရမလဲမှ Qty,
Requested Items To Be Transferred,လွှဲပြောင်းရန်မေတ္တာရပ်ခံပစ္စည်းများ,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1} ကညွှန
Therapy Session overlaps with {0},ကုထုံးတွေ့ဆုံဆွေးနွေးမှု {0} နှင့်ထပ်နေသည်,
Therapy Sessions Overlapping,ကုထုံးတွေ့ဆုံဆွေးနွေးပွဲ,
Therapy Plans,ကုထုံးအစီအစဉ်များ,
+"Item Code, warehouse, quantity are required on row {0}","item ကုဒ်, ဂိုဒေါင်, အရေအတွက်အတန်း {0} တွင်လိုအပ်သည်။",
+Get Items from Material Requests against this Supplier,ဒီပေးသွင်းသူဆန့်ကျင်ပစ္စည်းတောင်းဆိုမှုများမှပစ္စည်းများရယူပါ,
+Enable European Access,ဥရောပ Access ကိုဖွင့်,
+Creating Purchase Order ...,ဝယ်ယူမှုအမိန့်ကိုဖန်တီးခြင်း ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",အောက်ဖော်ပြပါပစ္စည်းများ၏ပုံမှန်ပေးသွင်းသူထံမှပေးသွင်းရွေးချယ်ပါ။ ရွေးချယ်မှုတွင်ရွေးချယ်ထားသောပေးသွင်းသူနှင့်သာသက်ဆိုင်သောပစ္စည်းများကိုသာ ၀ ယ်ရန်အမိန့်ပေးလိမ့်မည်။,
+Row #{}: You must select {} serial numbers for item {}.,Row # {} - item {} အတွက်နံပါတ်စဉ်ဆက်ရွေးပါ။,
diff --git a/erpnext/translations/nl.csv b/erpnext/translations/nl.csv
index 33b1098e17..fbadc02327 100644
--- a/erpnext/translations/nl.csv
+++ b/erpnext/translations/nl.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Downloaden als JSON,
End date can not be less than start date,Einddatum kan niet vroeger zijn dan startdatum,
For Default Supplier (Optional),Voor standaardleverancier (optioneel),
From date cannot be greater than To date,Vanaf de datum kan niet groter zijn dan tot nu toe,
-Get items from,Krijgen items uit,
Group by,Groeperen volgens,
In stock,Op voorraad,
Item name,Artikelnaam,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Inkooporder Trends,
Purchase Receipt Trends,Ontvangstbevestiging Trends,
Purchase Register,Inkoop Register,
Quotation Trends,Offerte Trends,
-Quoted Item Comparison,Geciteerd Item Vergelijking,
Received Items To Be Billed,Ontvangen artikelen nog te factureren,
Qty to Order,Aantal te bestellen,
Requested Items To Be Transferred,Aangevraagde Artikelen te Verplaatsen,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Het artikel waarnaar wordt
Therapy Session overlaps with {0},Therapiesessie overlapt met {0},
Therapy Sessions Overlapping,Therapiesessies overlappen elkaar,
Therapy Plans,Therapieplannen,
+"Item Code, warehouse, quantity are required on row {0}","Artikelcode, magazijn, aantal zijn vereist op rij {0}",
+Get Items from Material Requests against this Supplier,Artikelen ophalen van materiaalverzoeken tegen deze leverancier,
+Enable European Access,Schakel Europese toegang in,
+Creating Purchase Order ...,Inkooporder creëren ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Selecteer een leverancier uit de standaardleveranciers van de onderstaande items. Bij selectie wordt er alleen een inkooporder gemaakt voor artikelen van de geselecteerde leverancier.,
+Row #{}: You must select {} serial numbers for item {}.,Rij # {}: u moet {} serienummers voor artikel {} selecteren.,
diff --git a/erpnext/translations/no.csv b/erpnext/translations/no.csv
index bb8dbf25f3..150e5ca4a2 100644
--- a/erpnext/translations/no.csv
+++ b/erpnext/translations/no.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Last ned som JSON,
End date can not be less than start date,Sluttdato kan ikke være mindre enn startdato,
For Default Supplier (Optional),For standardleverandør (valgfritt),
From date cannot be greater than To date,Fra dato ikke kan være større enn To Date,
-Get items from,Få elementer fra,
Group by,Grupper etter,
In stock,På lager,
Item name,Navn,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Innkjøpsordre Trender,
Purchase Receipt Trends,Kvitteringen Trender,
Purchase Register,Kjøp Register,
Quotation Trends,Anførsels Trender,
-Quoted Item Comparison,Sitert Element Sammenligning,
Received Items To Be Billed,Mottatte elementer å bli fakturert,
Qty to Order,Antall å bestille,
Requested Items To Be Transferred,Etterspør elementene som skal overføres,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Varen det refereres til av
Therapy Session overlaps with {0},Behandlingsøkten overlapper med {0},
Therapy Sessions Overlapping,Terapisessioner overlapper hverandre,
Therapy Plans,Terapiplaner,
+"Item Code, warehouse, quantity are required on row {0}","Varekode, lager, antall kreves på rad {0}",
+Get Items from Material Requests against this Supplier,Få varer fra materialforespørsler mot denne leverandøren,
+Enable European Access,Aktiver europeisk tilgang,
+Creating Purchase Order ...,Opprette innkjøpsordre ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Velg en leverandør fra standardleverandørene av artiklene nedenfor. Ved valg vil det kun gjøres en innkjøpsordre mot varer som tilhører den valgte leverandøren.,
+Row #{}: You must select {} serial numbers for item {}.,Rad nr. {}: Du må velge {} serienumre for varen {}.,
diff --git a/erpnext/translations/pl.csv b/erpnext/translations/pl.csv
index c30d055e02..8340b7272f 100644
--- a/erpnext/translations/pl.csv
+++ b/erpnext/translations/pl.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Pobierz jako JSON,
End date can not be less than start date,"Data zakończenia nie może być wcześniejsza, niż data rozpoczęcia",
For Default Supplier (Optional),Dla dostawcy domyślnego (opcjonalnie),
From date cannot be greater than To date,Data od - nie może być późniejsza niż Data do,
-Get items from,Pobierz zawartość z,
Group by,Grupuj według,
In stock,W magazynie,
Item name,Nazwa pozycji,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Trendy Zamówienia Kupna,
Purchase Receipt Trends,Trendy Potwierdzenia Zakupu,
Purchase Register,Rejestracja Zakupu,
Quotation Trends,Trendy Wyceny,
-Quoted Item Comparison,Porównanie cytowany Item,
Received Items To Be Billed,Otrzymane przedmioty czekające na zaksięgowanie,
Qty to Order,Ilość do zamówienia,
Requested Items To Be Transferred,Proszę o Przetranferowanie Przedmiotów,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Pozycja, do której odwoł
Therapy Session overlaps with {0},Sesja terapeutyczna pokrywa się z {0},
Therapy Sessions Overlapping,Nakładanie się sesji terapeutycznych,
Therapy Plans,Plany terapii,
+"Item Code, warehouse, quantity are required on row {0}","Kod pozycji, magazyn, ilość są wymagane w wierszu {0}",
+Get Items from Material Requests against this Supplier,Pobierz pozycje z żądań materiałowych od tego dostawcy,
+Enable European Access,Włącz dostęp w Europie,
+Creating Purchase Order ...,Tworzenie zamówienia zakupu ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Wybierz dostawcę spośród domyślnych dostawców z poniższych pozycji. Po dokonaniu wyboru, Zamówienie zostanie złożone wyłącznie dla pozycji należących do wybranego Dostawcy.",
+Row #{}: You must select {} serial numbers for item {}.,Wiersz nr {}: należy wybrać {} numery seryjne dla towaru {}.,
diff --git a/erpnext/translations/ps.csv b/erpnext/translations/ps.csv
index d8a51aede8..1dcaf48d79 100644
--- a/erpnext/translations/ps.csv
+++ b/erpnext/translations/ps.csv
@@ -4238,7 +4238,6 @@ Download as JSON,د Json په څیر ډاونلوډ کړئ,
End date can not be less than start date,د پای نیټه نه شي کولای په پرتله د پیل نیټه کمه وي,
For Default Supplier (Optional),د اصلي عرضه کوونکي لپاره (اختیاري),
From date cannot be greater than To date,د نیټې څخه د نیټې څخه تر ډیره لوی نه وي,
-Get items from,له توکي ترلاسه کړئ,
Group by,ډله په,
In stock,په ګدام کښي,
Item name,د قالب نوم,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,پیري نظم رجحانات,
Purchase Receipt Trends,رانيول رسيد رجحانات,
Purchase Register,رانيول د نوم ثبتول,
Quotation Trends,د داوطلبۍ رجحانات,
-Quoted Item Comparison,له خولې د قالب پرتله,
Received Items To Be Billed,ترلاسه توکي چې د محاسبې ته شي,
Qty to Order,Qty ته اخلي.,
Requested Items To Be Transferred,غوښتنه سامان ته انتقال شي,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,هغه توکی چې د {0}
Therapy Session overlaps with {0},د درملنې ناسته د over 0 with سره پراخه کیږي,
Therapy Sessions Overlapping,د تهيريپي ناستې پوړونه,
Therapy Plans,د درملنې پلانونه,
+"Item Code, warehouse, quantity are required on row {0}",د توکي کوډ ، ګودام ، مقدار په قطار کې اړین دي {0},
+Get Items from Material Requests against this Supplier,د دې عرضه کونکي په وړاندې د موادو غوښتنو څخه توکي ترلاسه کړئ,
+Enable European Access,اروپایی لاسرسی وړ کړئ,
+Creating Purchase Order ...,د پیرود امر رامینځته کول ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",د لاندې شیانو ډیفالټ عرضه کونکو څخه یو عرضه کونکی غوره کړئ. په انتخاب کولو کې ، د پیرود امر به یوازې هغه انتخاب شوي چمتو کونکي پورې اړه لرونکي توکو په مقابل کې وي.,
+Row #{}: You must select {} serial numbers for item {}.,قطار # {}: تاسو باید د توکي {{لپاره سریال نمبرونه وټاکئ.,
diff --git a/erpnext/translations/pt.csv b/erpnext/translations/pt.csv
index 3289ef4f4c..3b8a0a0f43 100644
--- a/erpnext/translations/pt.csv
+++ b/erpnext/translations/pt.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Baixe como Json,
End date can not be less than start date,A Data de Término não pode ser mais recente que a Data de Início,
For Default Supplier (Optional),Para fornecedor padrão (opcional),
From date cannot be greater than To date,De data não pode ser maior que a data,
-Get items from,Obter itens de,
Group by,Agrupar Por,
In stock,Em estoque,
Item name,Nome do item,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Tendências de Ordens de Compra,
Purchase Receipt Trends,Tendências de Recibo de Compra,
Purchase Register,Registo de Compra,
Quotation Trends,Tendências de Cotação,
-Quoted Item Comparison,Comparação de Cotação de Item,
Received Items To Be Billed,Itens Recebidos a Serem Faturados,
Qty to Order,Qtd a Encomendar,
Requested Items To Be Transferred,Itens Solicitados A Serem Transferidos,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,O item referenciado por {0}
Therapy Session overlaps with {0},A sessão de terapia se sobrepõe a {0},
Therapy Sessions Overlapping,Sobreposição de sessões de terapia,
Therapy Plans,Planos de Terapia,
+"Item Code, warehouse, quantity are required on row {0}","Código do item, armazém, quantidade são necessários na linha {0}",
+Get Items from Material Requests against this Supplier,Obtenha itens de solicitações de materiais contra este fornecedor,
+Enable European Access,Habilitar acesso europeu,
+Creating Purchase Order ...,Criando pedido de compra ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Selecione um fornecedor dos fornecedores padrão dos itens abaixo. Na seleção, um pedido de compra será feito apenas para itens pertencentes ao fornecedor selecionado.",
+Row #{}: You must select {} serial numbers for item {}.,Nº da linha {}: você deve selecionar {} números de série para o item {}.,
diff --git a/erpnext/translations/pt_br.csv b/erpnext/translations/pt_br.csv
index 620fe273c4..cda5ee85f9 100644
--- a/erpnext/translations/pt_br.csv
+++ b/erpnext/translations/pt_br.csv
@@ -985,6 +985,7 @@ Purpose must be one of {0},Objetivo deve ser um dos {0},
Qty,Qtde,
Qty To Manufacture,Qtde para Fabricar,
Qty for {0},Qtde para {0},
+Quality,QCคุณภาพ,
Quality Goal.,Objetivos de Qualidade.,
Quality Inspection,Inspeção de Qualidade,
Quality Inspection: {0} is not submitted for the item: {1} in row {2},Inspeção de Qualidade: {0} não foi submetida para o item: {1} na linha {2},
diff --git a/erpnext/translations/ro.csv b/erpnext/translations/ro.csv
index 54211e2ee5..643b8c5c3e 100644
--- a/erpnext/translations/ro.csv
+++ b/erpnext/translations/ro.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Descărcați ca JSON,
End date can not be less than start date,Data de Incheiere nu poate fi anterioara Datei de Incepere,
For Default Supplier (Optional),Pentru furnizor implicit (opțional),
From date cannot be greater than To date,De la data nu poate fi mai mare decât la data,
-Get items from,Obține elemente din,
Group by,Grupul De,
In stock,In stoc,
Item name,Denumire Articol,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Comandă de aprovizionare Tendințe,
Purchase Receipt Trends,Tendințe Primirea de cumpărare,
Purchase Register,Cumpărare Inregistrare,
Quotation Trends,Cotație Tendințe,
-Quoted Item Comparison,Compararea Articol citat,
Received Items To Be Billed,Articole primite Pentru a fi facturat,
Qty to Order,Cantitate pentru comandă,
Requested Items To Be Transferred,Articole solicitate de transferat,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Elementul la care face refe
Therapy Session overlaps with {0},Sesiunea de terapie se suprapune cu {0},
Therapy Sessions Overlapping,Sesiunile de terapie se suprapun,
Therapy Plans,Planuri de terapie,
+"Item Code, warehouse, quantity are required on row {0}","Codul articolului, depozitul, cantitatea sunt necesare pe rândul {0}",
+Get Items from Material Requests against this Supplier,Obțineți articole din solicitările materiale împotriva acestui furnizor,
+Enable European Access,Activați accesul european,
+Creating Purchase Order ...,Se creează comanda de cumpărare ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Selectați un furnizor din furnizorii prestabiliți ai articolelor de mai jos. La selectare, o comandă de achiziție va fi făcută numai pentru articolele aparținând furnizorului selectat.",
+Row #{}: You must select {} serial numbers for item {}.,Rândul # {}: trebuie să selectați {} numere de serie pentru articol {}.,
diff --git a/erpnext/translations/ru.csv b/erpnext/translations/ru.csv
index 00a19b360c..7fcb7b08f7 100644
--- a/erpnext/translations/ru.csv
+++ b/erpnext/translations/ru.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Скачать как JSON,
End date can not be less than start date,"Дата окончания не может быть меньше, чем Дата начала",
For Default Supplier (Optional),Поставщик по умолчанию (необязательно),
From date cannot be greater than To date,"С даты не может быть больше, чем к дате",
-Get items from,Получить продукты от,
Group by,Group By,
In stock,В наличии,
Item name,Название продукта,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Заказ на покупку Тенденции,
Purchase Receipt Trends,Динамика Получения Поставок,
Purchase Register,Покупка Становиться на учет,
Quotation Trends,Динамика предложений,
-Quoted Item Comparison,Цитируется Сравнение товара,
Received Items To Be Billed,"Полученные товары, на которые нужно выписать счет",
Qty to Order,Кол-во в заказ,
Requested Items To Be Transferred,Запрашиваемые продукты к доставке,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"На товар, на ко
Therapy Session overlaps with {0},Сеанс терапии совпадает с {0},
Therapy Sessions Overlapping,Совмещение сеансов терапии,
Therapy Plans,Планы терапии,
+"Item Code, warehouse, quantity are required on row {0}","Код товара, склад, количество требуются в строке {0}",
+Get Items from Material Requests against this Supplier,Получить товары из запросов материалов к этому поставщику,
+Enable European Access,Включить европейский доступ,
+Creating Purchase Order ...,Создание заказа на поставку ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Выберите поставщика из списка поставщиков по умолчанию для позиций ниже. При выборе Заказ на поставку будет сделан в отношении товаров, принадлежащих только выбранному Поставщику.",
+Row #{}: You must select {} serial numbers for item {}.,Строка № {}: необходимо выбрать {} серийных номеров для позиции {}.,
diff --git a/erpnext/translations/rw.csv b/erpnext/translations/rw.csv
index 5995db1719..64591399af 100644
--- a/erpnext/translations/rw.csv
+++ b/erpnext/translations/rw.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Kuramo nka JSON,
End date can not be less than start date,Itariki yo kurangiriraho ntishobora kuba munsi yitariki yo gutangiriraho,
For Default Supplier (Optional),Kubisanzwe Bitanga (Bihitamo),
From date cannot be greater than To date,Kuva ku italiki ntishobora kurenza Kumunsi,
-Get items from,Shaka ibintu,
Group by,Itsinda by,
In stock,Mububiko,
Item name,Izina ryikintu,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Kugura Ibiciro,
Purchase Receipt Trends,Kugura inyemezabwishyu,
Purchase Register,Kwiyandikisha,
Quotation Trends,Imirongo,
-Quoted Item Comparison,Kugereranya Ikintu Kugereranya,
Received Items To Be Billed,Yakiriye Ibintu Byishyurwa,
Qty to Order,Qty gutumiza,
Requested Items To Be Transferred,Ibintu Byasabwe Kwimurwa,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Ikintu kivugwa na {0} - {1}
Therapy Session overlaps with {0},Isomo ryo kuvura ryuzuzanya na {0},
Therapy Sessions Overlapping,Amasomo yo kuvura,
Therapy Plans,Gahunda yo kuvura,
+"Item Code, warehouse, quantity are required on row {0}","Kode yikintu, ububiko, ingano irakenewe kumurongo {0}",
+Get Items from Material Requests against this Supplier,Shakisha Ibintu Mubisabwa Ibikoresho Kurwanya Utanga isoko,
+Enable European Access,Gushoboza Uburayi,
+Creating Purchase Order ...,Gushiraho gahunda yo kugura ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Hitamo Utanga isoko uhereye kubisanzwe bitanga ibintu hepfo. Muguhitamo, Iteka ryubuguzi rizakorwa kurwanya ibintu byatoranijwe gusa.",
+Row #{}: You must select {} serial numbers for item {}.,Umurongo # {}: Ugomba guhitamo {} nimero yuruhererekane kubintu {}.,
diff --git a/erpnext/translations/si.csv b/erpnext/translations/si.csv
index ba45b42006..690c47332d 100644
--- a/erpnext/translations/si.csv
+++ b/erpnext/translations/si.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSON ලෙස බාගන්න,
End date can not be less than start date,අවසන් දිනය ඇරඹුම් දිනය ඊට වඩා අඩු විය නොහැක,
For Default Supplier (Optional),Default සැපයුම්කරු සඳහා (විකල්ප),
From date cannot be greater than To date,දිනය සිට දිනට වඩා වැඩි විය නොහැක,
-Get items from,සිට භාණ්ඩ ලබා ගන්න,
Group by,කණ්ඩායම විසින්,
In stock,ගබඩාවේ ඇත,
Item name,අයිතමය නම,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,මිලදී ගැනීමේ නියෝගයක
Purchase Receipt Trends,මිලදී ගැනීම රිසිට්පත ප්රවණතා,
Purchase Register,මිලදී රෙජිස්ටර්,
Quotation Trends,උද්ධෘත ප්රවණතා,
-Quoted Item Comparison,උපුටා අයිතමය සංසන්දනය,
Received Items To Be Billed,ලැබී අයිතම බිල්පතක්,
Qty to Order,ඇණවුම් යවන ලද,
Requested Items To Be Transferred,ඉල්ලන අයිතම මාරු කර,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1 by විසින
Therapy Session overlaps with {0},චිකිත්සක සැසිය {0 with සමඟ අතිච්ඡාදනය වේ,
Therapy Sessions Overlapping,චිකිත්සක සැසි අතිච්ඡාදනය,
Therapy Plans,චිකිත්සක සැලසුම්,
+"Item Code, warehouse, quantity are required on row {0}","Code 0 row පේළියේ අයිතම කේතය, ගබඩාව, ප්රමාණය අවශ්ය වේ",
+Get Items from Material Requests against this Supplier,මෙම සැපයුම්කරුට එරෙහිව ද්රව්යමය ඉල්ලීම් වලින් අයිතම ලබා ගන්න,
+Enable European Access,යුරෝපීය ප්රවේශය සක්රීය කරන්න,
+Creating Purchase Order ...,මිලදී ගැනීමේ ඇණවුමක් නිර්මාණය කිරීම ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","පහත අයිතමවල පෙරනිමි සැපයුම්කරුවන්ගෙන් සැපයුම්කරුවෙකු තෝරන්න. තෝරාගැනීමේදී, තෝරාගත් සැපයුම්කරුට පමණක් අයත් භාණ්ඩවලට එරෙහිව මිලදී ගැනීමේ නියෝගයක් කරනු ලැබේ.",
+Row #{}: You must select {} serial numbers for item {}.,පේළිය # {}: ඔබ item item අයිතමය සඳහා {} අනුක්රමික අංක තෝරාගත යුතුය.,
diff --git a/erpnext/translations/sk.csv b/erpnext/translations/sk.csv
index d31adbadc2..cb4a7fed4b 100644
--- a/erpnext/translations/sk.csv
+++ b/erpnext/translations/sk.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Stiahnuť ako JSON,
End date can not be less than start date,Dátum ukončenia nemôže byť menší ako dátum začiatku,
For Default Supplier (Optional),Pre predvoleného dodávateľa (nepovinné),
From date cannot be greater than To date,Dátum OD nemôže byť väčší ako dátum DO,
-Get items from,Získať predmety z,
Group by,Seskupit podle,
In stock,Skladom,
Item name,Názov položky,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Nákupní objednávka trendy,
Purchase Receipt Trends,Doklad o koupi Trendy,
Purchase Register,Nákup Register,
Quotation Trends,Vývoje ponúk,
-Quoted Item Comparison,Citoval Položka Porovnanie,
Received Items To Be Billed,"Přijaté položek, které mají být účtovány",
Qty to Order,Množství k objednávce,
Requested Items To Be Transferred,Požadované položky mají být převedeny,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Položka, na ktorú odkazu
Therapy Session overlaps with {0},Terapeutické sedenie sa prekrýva s {0},
Therapy Sessions Overlapping,Terapeutické sedenia sa prekrývajú,
Therapy Plans,Terapeutické plány,
+"Item Code, warehouse, quantity are required on row {0}","V riadku {0} sa vyžaduje kód položky, sklad, množstvo",
+Get Items from Material Requests against this Supplier,Získajte položky z materiálových požiadaviek voči tomuto dodávateľovi,
+Enable European Access,Umožniť európsky prístup,
+Creating Purchase Order ...,Vytvára sa objednávka ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Z predvolených dodávateľov nižšie uvedených položiek vyberte dodávateľa. Pri výbere sa uskutoční objednávka iba na položky patriace vybranému dodávateľovi.,
+Row #{}: You must select {} serial numbers for item {}.,Riadok č. {}: Musíte zvoliť {} sériové čísla pre položku {}.,
diff --git a/erpnext/translations/sl.csv b/erpnext/translations/sl.csv
index 15795dccf0..8beec6be16 100644
--- a/erpnext/translations/sl.csv
+++ b/erpnext/translations/sl.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Prenesite kot JSON,
End date can not be less than start date,Datum konca ne sme biti krajši od začetnega datuma,
For Default Supplier (Optional),Za privzeto dobavitelja (neobvezno),
From date cannot be greater than To date,Od datuma ne more biti večje od datuma,
-Get items from,Pridobi artikle iz,
Group by,Skupina avtorja,
In stock,Na zalogi,
Item name,Ime predmeta,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Naročilnica Trendi,
Purchase Receipt Trends,Nakup Prejem Trendi,
Purchase Register,Nakup Register,
Quotation Trends,Trendi ponudb,
-Quoted Item Comparison,Citirano Točka Primerjava,
Received Items To Be Billed,Prejete Postavke placevali,
Qty to Order,Količina naročiti,
Requested Items To Be Transferred,Zahtevane blago prenaša,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Element, na katerega se sk
Therapy Session overlaps with {0},Seja terapije se prekriva z {0},
Therapy Sessions Overlapping,Terapijske seje se prekrivajo,
Therapy Plans,Načrti terapije,
+"Item Code, warehouse, quantity are required on row {0}","Koda artikla, skladišče, količina so obvezni v vrstici {0}",
+Get Items from Material Requests against this Supplier,Pridobite izdelke iz materialnih zahtevkov pri tem dobavitelju,
+Enable European Access,Omogoči evropski dostop,
+Creating Purchase Order ...,Ustvarjanje naročilnice ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Izberite dobavitelja med privzetimi dobavitelji spodnjih elementov. Po izbiri bo naročilnica narejena samo za izdelke, ki pripadajo izbranemu dobavitelju.",
+Row #{}: You must select {} serial numbers for item {}.,Vrstica # {}: Izbrati morate {} serijske številke za izdelek {}.,
diff --git a/erpnext/translations/sq.csv b/erpnext/translations/sq.csv
index 18d634e6c6..05aefa3602 100644
--- a/erpnext/translations/sq.csv
+++ b/erpnext/translations/sq.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Shkarkoni si JSON,
End date can not be less than start date,Data e mbarimit nuk mund të jetë më e shkurtër se data fillestare,
For Default Supplier (Optional),Për Furnizuesin e Parazgjedhur (fakultativ),
From date cannot be greater than To date,Nga Data nuk mund të jetë më i madh se deri më sot,
-Get items from,Të marrë sendet nga,
Group by,Grupi Nga,
In stock,Në gjendje,
Item name,Item Emri,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Rendit Blerje Trendet,
Purchase Receipt Trends,Trendet Receipt Blerje,
Purchase Register,Blerje Regjistrohu,
Quotation Trends,Kuotimit Trendet,
-Quoted Item Comparison,Cituar Item Krahasimi,
Received Items To Be Billed,Items marra Për të faturohet,
Qty to Order,Qty të Rendit,
Requested Items To Be Transferred,Items kërkuar të transferohet,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Artikulli i referuar nga {0
Therapy Session overlaps with {0},Sesioni i terapisë mbivendoset me {0},
Therapy Sessions Overlapping,Seancat e Terapisë Mbivendosen,
Therapy Plans,Planet e Terapisë,
+"Item Code, warehouse, quantity are required on row {0}","Kodi i artikullit, depoja, sasia kërkohen në rreshtin {0}",
+Get Items from Material Requests against this Supplier,Merrni Artikuj nga Kërkesat Materiale kundër këtij Furnizuesi,
+Enable European Access,Aktivizo Aksesin Evropian,
+Creating Purchase Order ...,Krijimi i urdhrit të blerjes ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Zgjidhni një furnitor nga furnitorët e parazgjedhur të artikujve më poshtë. Gjatë zgjedhjes, një Urdhër Blerje do të bëhet vetëm kundër artikujve që i përkasin vetëm Furnizuesit të zgjedhur.",
+Row #{}: You must select {} serial numbers for item {}.,Rreshti # {}: Duhet të zgjidhni {} numrat rendorë për artikullin {}.,
diff --git a/erpnext/translations/sr.csv b/erpnext/translations/sr.csv
index 44c49467b6..b507f74f09 100644
--- a/erpnext/translations/sr.csv
+++ b/erpnext/translations/sr.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Преузми као ЈСОН,
End date can not be less than start date,"Дата окончания не может быть меньше , чем Дата начала",
For Default Supplier (Optional),За подразумевани добављач,
From date cannot be greater than To date,Од датума не може бити већа него до сада,
-Get items from,Гет ставке из,
Group by,Група По,
In stock,На лагеру,
Item name,Назив,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Куповина Трендови Ордер,
Purchase Receipt Trends,Куповина Трендови Пријем,
Purchase Register,Куповина Регистрација,
Quotation Trends,Котировочные тенденции,
-Quoted Item Comparison,Цитирано артикла Поређење,
Received Items To Be Billed,Примљени артикала буду наплаћени,
Qty to Order,Количина по поруџбини,
Requested Items To Be Transferred,Тражени Артикли ће се пренети,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Ставка на коју
Therapy Session overlaps with {0},Сесија терапије се преклапа са {0},
Therapy Sessions Overlapping,Преклапање сесија терапије,
Therapy Plans,Планови терапије,
+"Item Code, warehouse, quantity are required on row {0}","Шифра артикла, складиште, количина су обавезни у реду {0}",
+Get Items from Material Requests against this Supplier,Узмите предмете од материјалних захтева против овог добављача,
+Enable European Access,Омогућити европски приступ,
+Creating Purchase Order ...,Креирање налога за куповину ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Изаберите добављача од задатих добављача доле наведених ставки. Након одабира, наруџбеница ће се извршити само за производе који припадају изабраном добављачу.",
+Row #{}: You must select {} serial numbers for item {}.,Ред # {}: Морате одабрати {} серијске бројеве за ставку {}.,
diff --git a/erpnext/translations/sr_sp.csv b/erpnext/translations/sr_sp.csv
index 3304dfcf26..5e7ae79781 100644
--- a/erpnext/translations/sr_sp.csv
+++ b/erpnext/translations/sr_sp.csv
@@ -649,7 +649,6 @@ No,Ne,
Yes,Da,
Chart of Accounts,Kontni plan,
Customer database.,Korisnička baza podataka,
-Get items from,Dodaj stavke iz,
Item name,Naziv artikla,
No employee found,Zaposleni nije pronađen,
Open Projects ,Otvoreni projekti,
diff --git a/erpnext/translations/sv.csv b/erpnext/translations/sv.csv
index 608bb46577..57e02792f4 100644
--- a/erpnext/translations/sv.csv
+++ b/erpnext/translations/sv.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Ladda ner som JSON,
End date can not be less than start date,Slutdatum kan inte vara mindre än startdatum,
For Default Supplier (Optional),För standardleverantör (tillval),
From date cannot be greater than To date,Från datum kan inte vara större än till datum,
-Get items from,Få objekt från,
Group by,Gruppera efter,
In stock,I lager,
Item name,Produktnamn,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Inköpsorder Trender,
Purchase Receipt Trends,Kvitto Trender,
Purchase Register,Inköpsregistret,
Quotation Trends,Offert Trender,
-Quoted Item Comparison,Citerade föremål Jämförelse,
Received Items To Be Billed,Mottagna objekt som ska faktureras,
Qty to Order,Antal till Ordern,
Requested Items To Be Transferred,Efterfrågade artiklar som ska överföras,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Varan som {0} - {1} referer
Therapy Session overlaps with {0},Terapisessionen överlappar med {0},
Therapy Sessions Overlapping,Terapisessioner överlappar varandra,
Therapy Plans,Terapiplaner,
+"Item Code, warehouse, quantity are required on row {0}","Artikelkod, lager, kvantitet krävs på rad {0}",
+Get Items from Material Requests against this Supplier,Få objekt från materialförfrågningar mot denna leverantör,
+Enable European Access,Aktivera europeisk åtkomst,
+Creating Purchase Order ...,Skapar inköpsorder ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",Välj en leverantör bland standardleverantörerna av artiklarna nedan. Vid val kommer en inköpsorder endast göras mot föremål som tillhör vald leverantör.,
+Row #{}: You must select {} serial numbers for item {}.,Rad nr {}: Du måste välja {} serienummer för artikeln {}.,
diff --git a/erpnext/translations/sw.csv b/erpnext/translations/sw.csv
index bffc1a8d47..3595727666 100644
--- a/erpnext/translations/sw.csv
+++ b/erpnext/translations/sw.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Pakua kama JSON,
End date can not be less than start date,Tarehe ya Mwisho haiwezi kuwa chini ya Tarehe ya Mwanzo,
For Default Supplier (Optional),Kwa Default Supplier (hiari),
From date cannot be greater than To date,Kutoka Tarehe haiwezi kuwa kubwa kuliko Tarehe,
-Get items from,Pata vitu kutoka,
Group by,Kikundi Kwa,
In stock,Katika hisa,
Item name,Jina la Kipengee,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Mwelekeo wa Utaratibu wa Ununuzi,
Purchase Receipt Trends,Ununuzi Mwelekeo wa Receipt,
Purchase Register,Daftari ya Ununuzi,
Quotation Trends,Mwelekeo wa Nukuu,
-Quoted Item Comparison,Ilipendekeza Kulinganishwa kwa Bidhaa,
Received Items To Be Billed,Vipokee Vipokee vya Kulipwa,
Qty to Order,Uchina kwa Amri,
Requested Items To Be Transferred,Vitu Vilivyoombwa Ili Kuhamishwa,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Bidhaa iliyorejelewa na {0}
Therapy Session overlaps with {0},Kipindi cha Tiba hupishana na {0},
Therapy Sessions Overlapping,Vikao vya Tiba vinaingiliana,
Therapy Plans,Mipango ya Tiba,
+"Item Code, warehouse, quantity are required on row {0}","Nambari ya Bidhaa, ghala, idadi inahitajika kwenye safu mlalo {0}",
+Get Items from Material Requests against this Supplier,Pata Vitu kutoka kwa Maombi ya Nyenzo dhidi ya Muuzaji huyu,
+Enable European Access,Washa Ufikiaji wa Uropa,
+Creating Purchase Order ...,Inaunda Agizo la Ununuzi ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Chagua Muuzaji kutoka kwa Wasambazaji Default wa vitu hapa chini. Wakati wa kuchagua, Agizo la Ununuzi litafanywa dhidi ya vitu vya Muuzaji aliyechaguliwa tu.",
+Row #{}: You must select {} serial numbers for item {}.,Mstari # {}: Lazima uchague {} nambari za serial za kipengee {}.,
diff --git a/erpnext/translations/ta.csv b/erpnext/translations/ta.csv
index b57668d8cf..100f0e9300 100644
--- a/erpnext/translations/ta.csv
+++ b/erpnext/translations/ta.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSON ஆக பதிவிறக்கவும்,
End date can not be less than start date,முடிவு தேதி தொடங்கும் நாள் விட குறைவாக இருக்க முடியாது,
For Default Supplier (Optional),இயல்புநிலை வழங்குநருக்கு (விரும்பினால்),
From date cannot be greater than To date,தேதி முதல் இன்று வரை விட முடியாது,
-Get items from,இருந்து பொருட்களை பெற,
Group by,குழு மூலம்,
In stock,கையிருப்பில்,
Item name,பொருள் பெயர்,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,ஆர்டர் போக்குகள் வா
Purchase Receipt Trends,ரிசிப்ட் போக்குகள் வாங்குவதற்கு,
Purchase Register,பதிவு வாங்குவதற்கு,
Quotation Trends,மேற்கோள் போக்குகள்,
-Quoted Item Comparison,மேற்கோள் காட்டப்பட்டது பொருள் ஒப்பீட்டு,
Received Items To Be Billed,கட்டணம் பெறப்படும் பொருட்கள்,
Qty to Order,அளவு ஒழுங்கிற்கு,
Requested Items To Be Transferred,மாற்றப்படுவதற்கு கோரப்பட்ட விடயங்கள்,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1 by ஆல் க
Therapy Session overlaps with {0},சிகிச்சை அமர்வு {0 with உடன் ஒன்றுடன் ஒன்று,
Therapy Sessions Overlapping,சிகிச்சை அமர்வுகள் ஒன்றுடன் ஒன்று,
Therapy Plans,சிகிச்சை திட்டங்கள்,
+"Item Code, warehouse, quantity are required on row {0}","Code 0 row வரிசையில் உருப்படி குறியீடு, கிடங்கு, அளவு தேவை",
+Get Items from Material Requests against this Supplier,இந்த சப்ளையருக்கு எதிரான பொருள் கோரிக்கைகளிலிருந்து பொருட்களைப் பெறுங்கள்,
+Enable European Access,ஐரோப்பிய அணுகலை இயக்கு,
+Creating Purchase Order ...,கொள்முதல் ஆணையை உருவாக்குதல் ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","கீழே உள்ள பொருட்களின் இயல்புநிலை சப்ளையர்களிடமிருந்து ஒரு சப்ளையரைத் தேர்ந்தெடுக்கவும். தேர்ந்தெடுக்கும் போது, தேர்ந்தெடுக்கப்பட்ட சப்ளையருக்கு மட்டுமே சொந்தமான பொருட்களுக்கு எதிராக கொள்முதல் ஆணை செய்யப்படும்.",
+Row #{}: You must select {} serial numbers for item {}.,வரிசை # {}: நீங்கள் item item உருப்படிக்கு {} வரிசை எண்களைத் தேர்ந்தெடுக்க வேண்டும்.,
diff --git a/erpnext/translations/te.csv b/erpnext/translations/te.csv
index e5ba32c2f5..047d07731e 100644
--- a/erpnext/translations/te.csv
+++ b/erpnext/translations/te.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSON గా డౌన్లోడ్ చేయండి,
End date can not be less than start date,ముగింపు తేదీ ప్రారంభ తేదీ కంటే తక్కువ ఉండకూడదు,
For Default Supplier (Optional),డిఫాల్ట్ సరఫరాదారు (ఐచ్ఛిక) కోసం,
From date cannot be greater than To date,తేదీ నుండి తేదీ వరకు ఎక్కువ ఉండకూడదు,
-Get items from,నుండి అంశాలను పొందండి,
Group by,గ్రూప్ ద్వారా,
In stock,అందుబాటులో ఉంది,
Item name,అంశం పేరు,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,ఆర్డర్ ట్రెండ్లులో క
Purchase Receipt Trends,కొనుగోలు రసీదులు ట్రెండ్లులో,
Purchase Register,కొనుగోలు నమోదు,
Quotation Trends,కొటేషన్ ట్రెండ్లులో,
-Quoted Item Comparison,ఉల్లేఖించిన అంశం పోలిక,
Received Items To Be Billed,స్వీకరించిన అంశాలు బిల్ టు,
Qty to Order,ఆర్డర్ చేయటం అంశాల,
Requested Items To Be Transferred,అభ్యర్థించిన అంశాలు బదిలీ,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1 by చే సూ
Therapy Session overlaps with {0},థెరపీ సెషన్ {0 with తో అతివ్యాప్తి చెందుతుంది,
Therapy Sessions Overlapping,థెరపీ సెషన్స్ అతివ్యాప్తి,
Therapy Plans,చికిత్స ప్రణాళికలు,
+"Item Code, warehouse, quantity are required on row {0}","Code 0 row వరుసలో అంశం కోడ్, గిడ్డంగి, పరిమాణం అవసరం",
+Get Items from Material Requests against this Supplier,ఈ సరఫరాదారుకు వ్యతిరేకంగా మెటీరియల్ అభ్యర్థనల నుండి అంశాలను పొందండి,
+Enable European Access,యూరోపియన్ ప్రాప్యతను ప్రారంభించండి,
+Creating Purchase Order ...,కొనుగోలు క్రమాన్ని సృష్టిస్తోంది ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","దిగువ వస్తువుల డిఫాల్ట్ సరఫరాదారుల నుండి సరఫరాదారుని ఎంచుకోండి. ఎంపికపై, ఎంచుకున్న సరఫరాదారుకు చెందిన వస్తువులపై మాత్రమే కొనుగోలు ఆర్డర్ చేయబడుతుంది.",
+Row #{}: You must select {} serial numbers for item {}.,అడ్డు వరుస # {}: మీరు తప్పక item} అంశం కోసం {} క్రమ సంఖ్యలను ఎంచుకోవాలి.,
diff --git a/erpnext/translations/th.csv b/erpnext/translations/th.csv
index 90c3a9a6ad..71233ec8fa 100644
--- a/erpnext/translations/th.csv
+++ b/erpnext/translations/th.csv
@@ -4238,7 +4238,6 @@ Download as JSON,ดาวน์โหลดเป็น JSON,
End date can not be less than start date,วันที่สิ้นสุด ไม่สามารถ จะน้อยกว่า วันเริ่มต้น,
For Default Supplier (Optional),สำหรับผู้จัดจำหน่ายเริ่มต้น (ตัวเลือก),
From date cannot be greater than To date,จากวันที่ไม่สามารถมากกว่าวันที่,
-Get items from,รับรายการจาก,
Group by,กลุ่มตาม,
In stock,มีสินค้าในสต๊อก,
Item name,ชื่อรายการ,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,แนวโน้มการสั่งซื้อ,
Purchase Receipt Trends,ซื้อแนวโน้มใบเสร็จรับเงิน,
Purchase Register,สั่งซื้อสมัครสมาชิก,
Quotation Trends,ใบเสนอราคา แนวโน้ม,
-Quoted Item Comparison,เปรียบเทียบรายการที่ยกมา,
Received Items To Be Billed,รายการที่ได้รับจะถูกเรียกเก็บเงิน,
Qty to Order,จำนวนการสั่งซื้อสินค้า,
Requested Items To Be Transferred,รายการที่ได้รับการร้องขอจะถูกถ่ายโอน,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,รายการที่
Therapy Session overlaps with {0},เซสชันการบำบัดทับซ้อนกับ {0},
Therapy Sessions Overlapping,การบำบัดที่ทับซ้อนกัน,
Therapy Plans,แผนการบำบัด,
+"Item Code, warehouse, quantity are required on row {0}",ต้องระบุรหัสสินค้าคลังสินค้าปริมาณในแถว {0},
+Get Items from Material Requests against this Supplier,รับรายการจากคำขอวัสดุกับซัพพลายเออร์นี้,
+Enable European Access,เปิดใช้งาน European Access,
+Creating Purchase Order ...,กำลังสร้างใบสั่งซื้อ ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",เลือกซัพพลายเออร์จากซัพพลายเออร์เริ่มต้นของรายการด้านล่าง ในการเลือกใบสั่งซื้อจะทำกับสินค้าที่เป็นของซัพพลายเออร์ที่เลือกเท่านั้น,
+Row #{}: You must select {} serial numbers for item {}.,แถว # {}: คุณต้องเลือก {} หมายเลขซีเรียลสำหรับรายการ {},
diff --git a/erpnext/translations/tr.csv b/erpnext/translations/tr.csv
index ebb754a217..9e7ba4d142 100644
--- a/erpnext/translations/tr.csv
+++ b/erpnext/translations/tr.csv
@@ -4238,7 +4238,6 @@ Download as JSON,JSON olarak indir,
End date can not be less than start date,"Bitiş Tarihi, Başlangıç Tarihinden daha az olamaz",
For Default Supplier (Optional),Varsayılan Tedarikçi için (İsteğe bağlı),
From date cannot be greater than To date,Tarihten itibaren tarihe kadardan ileride olamaz,
-Get items from,Öğeleri alın,
Group by,Grup tarafından,
In stock,Stokta var,
Item name,Ürün Adı,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Satın Alma Sipariş Analizi,
Purchase Receipt Trends,Satın Alma Teslim Alma Analizi,
Purchase Register,Satın alma kaydı,
Quotation Trends,Teklif Trendleri,
-Quoted Item Comparison,Kote Ürün Karşılaştırma,
Received Items To Be Billed,Faturalanacak Alınan Malzemeler,
Qty to Order,Sipariş Miktarı,
Requested Items To Be Transferred,Transfer edilmesi istenen Ürünler,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1} tarafından refer
Therapy Session overlaps with {0},"Terapi Oturumu, {0} ile çakışıyor",
Therapy Sessions Overlapping,Çakışan Terapi Seansları,
Therapy Plans,Tedavi Planları,
+"Item Code, warehouse, quantity are required on row {0}","{0}. Satırda Öğe Kodu, depo, miktar gerekli",
+Get Items from Material Requests against this Supplier,Bu Tedarikçiye Karşı Malzeme Taleplerinden Ürün Alın,
+Enable European Access,Avrupa Erişimini Etkinleştir,
+Creating Purchase Order ...,Satın Alma Siparişi Oluşturuluyor ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Aşağıdaki öğelerin Varsayılan Tedarikçilerinden bir Tedarikçi seçin. Seçim üzerine, yalnızca seçilen Tedarikçiye ait ürünler için bir Satın Alma Siparişi verilecektir.",
+Row #{}: You must select {} serial numbers for item {}.,Satır # {}: {} öğesi için {} seri numaralarını seçmelisiniz.,
diff --git a/erpnext/translations/uk.csv b/erpnext/translations/uk.csv
index b43346c414..53e2df518c 100644
--- a/erpnext/translations/uk.csv
+++ b/erpnext/translations/uk.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Завантажити як JSON,
End date can not be less than start date,"Дата завершення не може бути меншою, ніж Дата початку",
For Default Supplier (Optional),Для постачальника за замовчуванням (необов'язково),
From date cannot be greater than To date,"Від дати не може бути більше, ніж Дата",
-Get items from,Отримати елементи з,
Group by,Групувати за,
In stock,В наявності,
Item name,Назва виробу,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Динаміка Замовлень на придбанн
Purchase Receipt Trends,Тренд прихідних накладних,
Purchase Register,Реєстр закупівель,
Quotation Trends,Тренд пропозицій,
-Quoted Item Comparison,Цитується Порівняння товару,
Received Items To Be Billed,"Отримані позиції, на які не виставлені рахунки",
Qty to Order,К-сть для замовлення,
Requested Items To Be Transferred,"Номенклатура, що ми замовили але не отримали",
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,"Позиція, на як
Therapy Session overlaps with {0},Сеанс терапії накладається на {0},
Therapy Sessions Overlapping,"Сеанси терапії, що перекриваються",
Therapy Plans,Плани терапії,
+"Item Code, warehouse, quantity are required on row {0}","Код товару, склад, кількість вказуються в рядку {0}",
+Get Items from Material Requests against this Supplier,Отримуйте предмети від матеріальних запитів у цього постачальника,
+Enable European Access,Увімкнути європейський доступ,
+Creating Purchase Order ...,Створення замовлення на придбання ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Виберіть постачальника з постачальників за замовчуванням із наведених нижче пунктів. При виборі замовлення на замовлення буде зроблено лише щодо товарів, що належать обраному Постачальнику.",
+Row #{}: You must select {} serial numbers for item {}.,Рядок № {}: Ви повинні вибрати {} серійні номери для товару {}.,
diff --git a/erpnext/translations/ur.csv b/erpnext/translations/ur.csv
index 7d9cacd272..aaaef5895f 100644
--- a/erpnext/translations/ur.csv
+++ b/erpnext/translations/ur.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Json کی طرح ڈاؤن لوڈ کریں۔,
End date can not be less than start date,ختم ہونے کی تاریخ شروع کرنے کی تاریخ کے مقابلے میں کم نہیں ہو سکتا,
For Default Supplier (Optional),ڈیفالٹ سپلائر کے لئے (اختیاری),
From date cannot be greater than To date,تاریخ سے تاریخ سے زیادہ نہیں ہوسکتی ہے,
-Get items from,سے اشیاء حاصل,
Group by,گروپ سے,
In stock,اسٹاک میں,
Item name,نام شے,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,آرڈر رجحانات خریدیں,
Purchase Receipt Trends,خریداری کی رسید رجحانات,
Purchase Register,خریداری رجسٹر,
Quotation Trends,کوٹیشن رجحانات,
-Quoted Item Comparison,نقل آئٹم موازنہ,
Received Items To Be Billed,موصول ہونے والی اشیاء بل بھیجا جائے کرنے کے لئے,
Qty to Order,آرڈر کی مقدار,
Requested Items To Be Transferred,درخواست کی اشیاء منتقل کیا جائے,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,جس شے کا حوالہ {
Therapy Session overlaps with {0},تھراپی سیشن {0 with کے ساتھ اوورلیپ ہوتا ہے,
Therapy Sessions Overlapping,تھراپی سیشن اوور لیپنگ,
Therapy Plans,تھراپی کے منصوبے,
+"Item Code, warehouse, quantity are required on row {0}",آئٹم کوڈ ، گودام ، مقدار قطار میں ضروری ہے {0},
+Get Items from Material Requests against this Supplier,اس سپلائر کے خلاف مادی درخواستوں سے اشیا حاصل کریں,
+Enable European Access,یورپی رسائی کو فعال کریں,
+Creating Purchase Order ...,خریداری کا آرڈر تشکیل دے رہا ہے…,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",ذیل میں آئٹمز کے ڈیفالٹ سپلائرز میں سے ایک سپلائر منتخب کریں۔ انتخاب پر ، خریداری کا آرڈر صرف منتخب کردہ سپلائر سے متعلقہ اشیاء کے خلاف کیا جائے گا۔,
+Row #{}: You must select {} serial numbers for item {}.,قطار # {}: آپ کو آئٹم for for کے لئے}} سیریل نمبرز منتخب کرنا ہوں گے۔,
diff --git a/erpnext/translations/uz.csv b/erpnext/translations/uz.csv
index f05b582624..c983797aae 100644
--- a/erpnext/translations/uz.csv
+++ b/erpnext/translations/uz.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Json sifatida yuklab oling,
End date can not be less than start date,Tugash sanasi Boshlanish sanasidan past bo'lishi mumkin emas,
For Default Supplier (Optional),Standart yetkazib beruvchi (ixtiyoriy),
From date cannot be greater than To date,Sana Sana Sana uchun katta bo'lishi mumkin emas,
-Get items from,Elementlarni oling,
Group by,Guruh tomonidan,
In stock,Omborda mavjud; sotuvda mavjud,
Item name,Mavzu nomi,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Buyurtma tendentsiyalarini sotib olish,
Purchase Receipt Trends,Qabul rejasi xaridlari,
Purchase Register,Xarid qilish Register,
Quotation Trends,Iqtiboslar tendentsiyalari,
-Quoted Item Comparison,Qisqartirilgan ob'ektni solishtirish,
Received Items To Be Billed,Qabul qilinadigan buyumlar,
Qty to Order,Buyurtma miqdori,
Requested Items To Be Transferred,Talab qilingan narsalarni yuborish,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0} - {1} tomonidan havola
Therapy Session overlaps with {0},Terapiya mashg'uloti {0} bilan takrorlanadi,
Therapy Sessions Overlapping,Terapiya mashg'ulotlari bir-birini qoplaydi,
Therapy Plans,Terapiya rejalari,
+"Item Code, warehouse, quantity are required on row {0}","Mahsulot kodi, ombor, miqdori {0} qatorida talab qilinadi",
+Get Items from Material Requests against this Supplier,Ushbu etkazib beruvchiga qarshi material talablaridan narsalarni oling,
+Enable European Access,Evropaga kirishni yoqish,
+Creating Purchase Order ...,Xarid buyurtmasi yaratilmoqda ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Quyidagi mahsulotlarning standart etkazib beruvchilardan etkazib beruvchini tanlang. Tanlovda, faqat tanlangan etkazib beruvchiga tegishli buyumlarga qarshi Sotib olish to'g'risida buyurtma beriladi.",
+Row #{}: You must select {} serial numbers for item {}.,Qator # {}: Siz {} element uchun seriya raqamlarini {} tanlashingiz kerak.,
diff --git a/erpnext/translations/vi.csv b/erpnext/translations/vi.csv
index a76c9fdec6..03ff2ccc38 100644
--- a/erpnext/translations/vi.csv
+++ b/erpnext/translations/vi.csv
@@ -4238,7 +4238,6 @@ Download as JSON,Tải xuống dưới dạng JSON,
End date can not be less than start date,Ngày kết thúc không thể nhỏ hơn Bắt đầu ngày,
For Default Supplier (Optional),Đối với nhà cung cấp mặc định (Tùy chọn),
From date cannot be greater than To date,"""Từ ngày"" không có thể lớn hơn ""Đến ngày""",
-Get items from,Lấy dữ liệu từ,
Group by,Nhóm theo,
In stock,Trong kho,
Item name,Tên hàng,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,Xu hướng mua hàng,
Purchase Receipt Trends,Xu hướng của biên lai nhận hàng,
Purchase Register,Đăng ký mua,
Quotation Trends,Các Xu hướng dự kê giá,
-Quoted Item Comparison,So sánh mẫu hàng đã được báo giá,
Received Items To Be Billed,Những mẫu hàng nhận được để lập hóa đơn,
Qty to Order,Số lượng đặt hàng,
Requested Items To Be Transferred,Mục yêu cầu được chuyển giao,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,Mặt hàng được tham c
Therapy Session overlaps with {0},Phiên trị liệu trùng lặp với {0},
Therapy Sessions Overlapping,Các phiên trị liệu chồng chéo,
Therapy Plans,Kế hoạch trị liệu,
+"Item Code, warehouse, quantity are required on row {0}","Mã hàng, kho, số lượng là bắt buộc trên hàng {0}",
+Get Items from Material Requests against this Supplier,Nhận các mặt hàng từ các Yêu cầu Vật liệu đối với Nhà cung cấp này,
+Enable European Access,Bật quyền truy cập Châu Âu,
+Creating Purchase Order ...,Tạo Đơn đặt hàng ...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Chọn Nhà cung cấp từ các Nhà cung cấp mặc định của các mục dưới đây. Khi lựa chọn, Đơn đặt hàng sẽ được thực hiện đối với các mặt hàng chỉ thuộc về Nhà cung cấp đã chọn.",
+Row #{}: You must select {} serial numbers for item {}.,Hàng # {}: Bạn phải chọn {} số sê-ri cho mặt hàng {}.,
diff --git a/erpnext/translations/zh.csv b/erpnext/translations/zh.csv
index 97c69fecb9..716f1f2f31 100644
--- a/erpnext/translations/zh.csv
+++ b/erpnext/translations/zh.csv
@@ -4238,7 +4238,6 @@ Download as JSON,下载为JSON,
End date can not be less than start date,结束日期不能小于开始日期,
For Default Supplier (Optional),对于默认供应商(可选),
From date cannot be greater than To date,从日期不能大于到日期,
-Get items from,从...获取物料,
Group by,分组基于,
In stock,有现货,
Item name,物料名称,
@@ -8549,7 +8548,6 @@ Purchase Order Trends,采购订单趋势,
Purchase Receipt Trends,采购收货趋势,
Purchase Register,采购台帐,
Quotation Trends,报价趋势,
-Quoted Item Comparison,项目报价比较,
Received Items To Be Billed,待开费用清单已收货物料,
Qty to Order,待下单数量,
Requested Items To Be Transferred,已申请待移转物料,
@@ -9834,3 +9832,9 @@ The item referenced by {0} - {1} is already invoiced,{0}-{1}引用的商品已
Therapy Session overlaps with {0},治疗会话与{0}重叠,
Therapy Sessions Overlapping,治疗会议重叠,
Therapy Plans,治疗计划,
+"Item Code, warehouse, quantity are required on row {0}",在第{0}行中需要提供物料代码,仓库,数量,
+Get Items from Material Requests against this Supplier,从针对此供应商的物料请求中获取物料,
+Enable European Access,启用欧洲访问,
+Creating Purchase Order ...,创建采购订单...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",从以下各项的默认供应商中选择供应商。选择后,将针对仅属于所选供应商的项目下达采购订单。,
+Row #{}: You must select {} serial numbers for item {}.,行号{}:您必须为项目{}选择{}序列号。,
diff --git a/erpnext/translations/zh_tw.csv b/erpnext/translations/zh_tw.csv
index 2de0df0d55..1cc7d8773e 100644
--- a/erpnext/translations/zh_tw.csv
+++ b/erpnext/translations/zh_tw.csv
@@ -3967,7 +3967,6 @@ Download as JSON,下載為JSON,
End date can not be less than start date,結束日期不能小於開始日期,
For Default Supplier (Optional),對於默認供應商(可選),
From date cannot be greater than To date,起始日期不能大於結束日期,
-Get items from,取得項目來源,
Group by,集團通過,
In stock,有現貨,
Item name,項目名稱,
@@ -7967,7 +7966,6 @@ Purchase Order Trends,採購訂單趨勢,
Purchase Receipt Trends,採購入庫趨勢,
Purchase Register,購買註冊,
Quotation Trends,報價趨勢,
-Quoted Item Comparison,項目報價比較,
Received Items To Be Billed,待付款的收受品項,
Qty to Order,訂購數量,
Requested Items To Be Transferred,將要轉倉的需求項目,
@@ -9136,3 +9134,9 @@ The item referenced by {0} - {1} is already invoiced,{0}-{1}引用的商品已
Therapy Session overlaps with {0},治療會話與{0}重疊,
Therapy Sessions Overlapping,治療會議重疊,
Therapy Plans,治療計劃,
+"Item Code, warehouse, quantity are required on row {0}",在第{0}行中需要提供物料代碼,倉庫,數量,
+Get Items from Material Requests against this Supplier,從針對此供應商的物料請求中獲取物料,
+Enable European Access,啟用歐洲訪問,
+Creating Purchase Order ...,創建採購訂單...,
+"Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.",從以下各項的默認供應商中選擇供應商。選擇後,將針對僅屬於所選供應商的項目下達採購訂單。,
+Row #{}: You must select {} serial numbers for item {}.,行號{}:您必須為項目{}選擇{}序列號。,