Merge branch 'develop' of https://github.com/frappe/erpnext into custom_append_to

This commit is contained in:
Himanshu Warekar 2020-04-06 11:34:27 +05:30
commit c207c457a5
220 changed files with 2413 additions and 4596 deletions

View File

@ -15,18 +15,18 @@
ERPNext as a monolith includes the following areas for managing businesses: ERPNext as a monolith includes the following areas for managing businesses:
1. [Accounting](https://erpnext.com/docs/user/manual/en/accounts) 1. [Accounting](https://erpnext.com/open-source-accounting)
1. [Inventory](https://erpnext.com/docs/user/manual/en/stock) 1. [Inventory](https://erpnext.com/distribution/inventory-management-system)
1. [CRM](https://erpnext.com/docs/user/manual/en/CRM) 1. [CRM](https://erpnext.com/open-source-crm)
1. [Sales](https://erpnext.com/docs/user/manual/en/selling) 1. [Sales](https://erpnext.com/open-source-sales-purchase)
1. [Purchase](https://erpnext.com/docs/user/manual/en/buying) 1. [Purchase](https://erpnext.com/open-source-sales-purchase)
1. [HRMS](https://erpnext.com/docs/user/manual/en/human-resources) 1. [HRMS](https://erpnext.com/open-source-hrms)
1. [Project Management](https://erpnext.com/docs/user/manual/en/projects) 1. [Project Management](https://erpnext.com/open-source-projects)
1. [Support](https://erpnext.com/docs/user/manual/en/support) 1. [Support](https://erpnext.com/open-source-help-desk-software)
1. [Asset Management](https://erpnext.com/docs/user/manual/en/asset) 1. [Asset Management](https://erpnext.com/open-source-asset-management-software)
1. [Quality Management](https://erpnext.com/docs/user/manual/en/quality-management) 1. [Quality Management](https://erpnext.com/docs/user/manual/en/quality-management)
1. [Manufacturing](https://erpnext.com/docs/user/manual/en/manufacturing) 1. [Manufacturing](https://erpnext.com/open-source-manufacturing-erp-software)
1. [Website Management](https://erpnext.com/docs/user/manual/en/website) 1. [Website Management](https://erpnext.com/open-source-website-builder-software)
1. [Customize ERPNext](https://erpnext.com/docs/user/manual/en/customize-erpnext) 1. [Customize ERPNext](https://erpnext.com/docs/user/manual/en/customize-erpnext)
1. [And More](https://erpnext.com/docs/user/manual/en/) 1. [And More](https://erpnext.com/docs/user/manual/en/)

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides from erpnext.hooks import regional_overrides
from frappe.utils import getdate from frappe.utils import getdate
__version__ = '12.2.0' __version__ = '12.0.0-dev'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@ -26,7 +26,7 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d
to_date = chart.to_date to_date = chart.to_date
timegrain = chart.time_interval timegrain = chart.time_interval
filters = frappe.parse_json(chart.filters_json) filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
account = filters.get("account") account = filters.get("account")
company = filters.get("company") company = filters.get("company")

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_copy": 1, "allow_copy": 1,
"allow_import": 1, "allow_import": 1,
"creation": "2013-01-30 12:49:46", "creation": "2013-01-30 12:49:46",
@ -196,10 +197,13 @@
], ],
"icon": "fa fa-money", "icon": "fa fa-money",
"idx": 1, "idx": 1,
"modified": "2019-10-10 19:10:02.967554", "is_tree": 1,
"links": [],
"modified": "2020-03-18 17:57:52.063233",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Account", "name": "Account",
"nsm_parent_field": "parent_account",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@ -102,7 +102,7 @@ class Account(NestedSet):
if not frappe.db.get_value("Account", if not frappe.db.get_value("Account",
{'account_name': self.account_name, 'company': ancestors[0]}, 'name'): {'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0])) frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
else: elif self.parent_account:
descendants = get_descendants_of('Company', self.company) descendants = get_descendants_of('Company', self.company)
if not descendants: return if not descendants: return
parent_acc_name_map = {} parent_acc_name_map = {}

View File

@ -663,16 +663,22 @@
"account_number": "1400" "account_number": "1400"
}, },
"Abziehbare Vorsteuer 7 %": { "Abziehbare Vorsteuer 7 %": {
"account_number": "1401" "account_number": "1401",
"account_type": "Tax",
"tax_rate": 7.0
}, },
"Abziehbare Vorsteuer aus innergem. Erwerb": { "Abziehbare Vorsteuer aus innergem. Erwerb": {
"account_number": "1402" "account_number": "1402"
}, },
"Abziehbare Vorsteuer aus innergem. Erwerb 19%": { "Abziehbare Vorsteuer aus innergem. Erwerb 19%": {
"account_number": "1404" "account_number": "1404",
"account_type": "Tax",
"tax_rate": 19.0
}, },
"Abziehbare Vorsteuer 19 %": { "Abziehbare Vorsteuer 19 %": {
"account_number": "1406" "account_number": "1406",
"account_type": "Tax",
"tax_rate": 19.0
}, },
"Abziehbare Vorsteuer nach \u00a7 13b UStG 19 %": { "Abziehbare Vorsteuer nach \u00a7 13b UStG 19 %": {
"account_number": "1407" "account_number": "1407"
@ -911,17 +917,31 @@
"is_group": 1, "is_group": 1,
"I - Gezeichnetes Kapital": { "I - Gezeichnetes Kapital": {
"account_type": "Equity", "account_type": "Equity",
"is_group": 1,
"Gezeichnetes Kapital": {
"account_type": "Equity",
"account_number": "2900"
},
"Ausstehende Einlagen auf das gezeichnete Kapital": {
"account_number": "2910",
"is_group": 1 "is_group": 1
}
}, },
"II - Kapitalr\u00fccklage": { "II - Kapitalr\u00fccklage": {
"account_type": "Equity", "account_type": "Equity",
"is_group": 1 "is_group": 1,
"Kapitalr\u00fccklage": {
"account_number": "2920"
}
}, },
"III - Gewinnr\u00fccklagen": { "III - Gewinnr\u00fccklagen": {
"account_type": "Equity", "account_type": "Equity",
"1 - gesetzliche R\u00fccklage": { "1 - gesetzliche R\u00fccklage": {
"account_type": "Equity", "account_type": "Equity",
"is_group": 1 "is_group": 1,
"Gesetzliche R\u00fccklage": {
"account_number": "2930"
}
}, },
"2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": { "2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
"account_type": "Equity", "account_type": "Equity",
@ -929,7 +949,10 @@
}, },
"3 - satzungsm\u00e4\u00dfige R\u00fccklagen": { "3 - satzungsm\u00e4\u00dfige R\u00fccklagen": {
"account_type": "Equity", "account_type": "Equity",
"is_group": 1 "is_group": 1,
"Satzungsm\u00e4\u00dfige R\u00fccklagen": {
"account_number": "2950"
}
}, },
"4 - andere Gewinnr\u00fccklagen": { "4 - andere Gewinnr\u00fccklagen": {
"account_type": "Equity", "account_type": "Equity",
@ -963,7 +986,13 @@
}, },
"IV - Gewinnvortrag/Verlustvortrag": { "IV - Gewinnvortrag/Verlustvortrag": {
"account_type": "Equity", "account_type": "Equity",
"is_group": 1 "is_group": 1,
"Gewinnvortrag vor Verwendung": {
"account_number": "2970"
},
"Verlustvortrag vor Verwendung": {
"account_number": "2978"
}
}, },
"V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": { "V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": {
"account_type": "Equity", "account_type": "Equity",
@ -1488,17 +1517,21 @@
}, },
"Umsatzsteuer 7 %": { "Umsatzsteuer 7 %": {
"account_number": "3801", "account_number": "3801",
"account_type": "Tax" "account_type": "Tax",
"tax_rate": 7.0
}, },
"Umsatzsteuer aus innergem. Erwerb": { "Umsatzsteuer aus innergem. Erwerb": {
"account_number": "3802" "account_number": "3802"
}, },
"Umsatzsteuer aus innergem. Erwerb 19 %": { "Umsatzsteuer aus innergem. Erwerb 19 %": {
"account_number": "3804" "account_number": "3804",
"account_type": "Tax",
"tax_rate": 19.0
}, },
"Umsatzsteuer 19 %": { "Umsatzsteuer 19 %": {
"account_number": "3806", "account_number": "3806",
"account_type": "Tax" "account_type": "Tax",
"tax_rate": 19.0
}, },
"Umsatzsteuer aus im Inland steuerpfl. EU-Lieferungen": { "Umsatzsteuer aus im Inland steuerpfl. EU-Lieferungen": {
"account_number": "3807" "account_number": "3807"
@ -1654,6 +1687,14 @@
"account_number": "4300", "account_number": "4300",
"account_type": "Income Account" "account_type": "Income Account"
}, },
"Erl\u00f6se 16 % USt": {
"account_number": "4340",
"account_type": "Income Account"
},
"Erl\u00f6se 19 % USt": {
"account_number": "4400",
"account_type": "Income Account"
},
"Erl\u00f6se aus im Inland steuerpfl. EU-Lieferungen 7 % USt": { "Erl\u00f6se aus im Inland steuerpfl. EU-Lieferungen 7 % USt": {
"account_number": "4310" "account_number": "4310"
}, },
@ -1681,19 +1722,6 @@
"Erl\u00f6se aus im anderen EU-Land steuerbaren Leistungen, im Inland nicht steuerbare Ums\u00e4tze": { "Erl\u00f6se aus im anderen EU-Land steuerbaren Leistungen, im Inland nicht steuerbare Ums\u00e4tze": {
"account_number": "4339" "account_number": "4339"
}, },
"Erl\u00f6se 16 % USt (Gruppe)": {
"is_group": 1,
"Erl\u00f6se 16 % USt": {
"account_number": "4340"
}
},
"Erl\u00f6se 19 % USt (Gruppe)": {
"is_group": 1,
"Erl\u00f6se 19 % USt": {
"account_number": "4400",
"account_type": "Income Account"
}
},
"Grundst\u00fccksertr\u00e4ge (Gruppe)": { "Grundst\u00fccksertr\u00e4ge (Gruppe)": {
"is_group": 1, "is_group": 1,
"Grundst\u00fccksertr\u00e4ge": { "Grundst\u00fccksertr\u00e4ge": {
@ -1752,14 +1780,12 @@
"2 - Herstellungskosten der zur Erzielung der Umsatzerl\u00f6se erbrachten Leistungen": { "2 - Herstellungskosten der zur Erzielung der Umsatzerl\u00f6se erbrachten Leistungen": {
"root_type": "Expense", "root_type": "Expense",
"is_group": 1, "is_group": 1,
"Herstellungskosten (Gruppe)": {
"Herstellungskosten": { "Herstellungskosten": {
"account_number": "6990", "account_number": "6990",
"account_type": "Cost of Goods Sold" "account_type": "Cost of Goods Sold"
}, },
"Herstellungskosten: Schwund": { "Herstellungskosten: Schwund": {
"account_type": "Stock Adjustment" "account_type": "Stock Adjustment"
}
}, },
"Aufwendungen f. Roh-, Hilfs- und Betriebsstoffe und f. bezogene Waren": { "Aufwendungen f. Roh-, Hilfs- und Betriebsstoffe und f. bezogene Waren": {
"account_number": "5000", "account_number": "5000",
@ -1935,6 +1961,7 @@
}, },
"Nachl\u00e4sse aus innergem. Erwerb 15 % Vorsteuer und 15 % Umsatzsteuer": { "Nachl\u00e4sse aus innergem. Erwerb 15 % Vorsteuer und 15 % Umsatzsteuer": {
"account_number": "5727" "account_number": "5727"
}
}, },
"Erhaltene Skonti (Gruppe)": { "Erhaltene Skonti (Gruppe)": {
"is_group": 1, "is_group": 1,
@ -1977,7 +2004,6 @@
"Erh. Skonti aus Erwerb Waren als letzter Abnehmer innerh. Dreiecksgesch. 19% Vorst. u. 19% Ust.": { "Erh. Skonti aus Erwerb Waren als letzter Abnehmer innerh. Dreiecksgesch. 19% Vorst. u. 19% Ust.": {
"account_number": "5793" "account_number": "5793"
} }
}
}, },
"Bezugsnebenkosten (Gruppe)": { "Bezugsnebenkosten (Gruppe)": {
"is_group": 1, "is_group": 1,
@ -2407,8 +2433,6 @@
"Erl\u00f6se aus Verk\u00e4ufen Sachanlageverm\u00f6gen (bei Buchgewinn)": { "Erl\u00f6se aus Verk\u00e4ufen Sachanlageverm\u00f6gen (bei Buchgewinn)": {
"account_number": "4849" "account_number": "4849"
}, },
"Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn) (Gruppe)": {
"is_group": 1,
"Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": { "Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": {
"account_number": "4850" "account_number": "4850"
}, },
@ -2429,7 +2453,6 @@
}, },
"Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": { "Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": {
"account_number": "4858" "account_number": "4858"
}
}, },
"Ertr\u00e4ge aus Zuschreibungen des Sachanlageverm\u00f6gens": { "Ertr\u00e4ge aus Zuschreibungen des Sachanlageverm\u00f6gens": {
"account_number": "4910", "account_number": "4910",
@ -2552,8 +2575,6 @@
"Entnahme von Gegenst\u00e4nden ohne USt": { "Entnahme von Gegenst\u00e4nden ohne USt": {
"account_number": "4605" "account_number": "4605"
}, },
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt (Gruppe)": {
"is_group": 1,
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": { "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": {
"account_number": "4630" "account_number": "4630"
}, },
@ -2565,7 +2586,6 @@
}, },
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": { "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": {
"account_number": "4639" "account_number": "4639"
}
}, },
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 19 % USt (Gruppe)": { "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 19 % USt (Gruppe)": {
"is_group": 1, "is_group": 1,
@ -2603,14 +2623,11 @@
"Unentgeltliche Zuwendung von Gegenst\u00e4nden ohne USt": { "Unentgeltliche Zuwendung von Gegenst\u00e4nden ohne USt": {
"account_number": "4689" "account_number": "4689"
}, },
"Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze) (Gruppe)": {
"is_group": 1,
"Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": { "Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": {
"account_number": "4690" "account_number": "4690"
}, },
"Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": { "Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": {
"account_number": "4695" "account_number": "4695"
}
}, },
"Au\u00dferordentliche Ertr\u00e4ge (Gruppe)": { "Au\u00dferordentliche Ertr\u00e4ge (Gruppe)": {
"is_group": 1, "is_group": 1,
@ -2620,8 +2637,6 @@
"Au\u00dferordentliche Ertr\u00e4ge finanzwirksam": { "Au\u00dferordentliche Ertr\u00e4ge finanzwirksam": {
"account_number": "7401" "account_number": "7401"
}, },
"Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam (Gruppe)": {
"is_group": 1,
"Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": { "Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": {
"account_number": "7450" "account_number": "7450"
}, },
@ -2636,10 +2651,7 @@
}, },
"Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": { "Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": {
"account_number": "7454" "account_number": "7454"
}
}, },
"Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften (Gruppe)": {
"is_group": 1,
"Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": { "Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": {
"account_number": "7460" "account_number": "7460"
}, },
@ -2656,7 +2668,6 @@
"account_number": "7464" "account_number": "7464"
} }
} }
}
}, },
"7 - sonstige betriebliche Aufwendungen": { "7 - sonstige betriebliche Aufwendungen": {
"root_type": "Expense", "root_type": "Expense",
@ -2692,6 +2703,7 @@
}, },
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": { "Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": {
"account_number": "4729" "account_number": "4729"
}
}, },
"Gew\u00e4hrte Skonti (Gruppe)": { "Gew\u00e4hrte Skonti (Gruppe)": {
"is_group": 1, "is_group": 1,
@ -2726,6 +2738,8 @@
"account_number": "4748" "account_number": "4748"
} }
}, },
"Gew\u00e4hrte Boni (Gruppe)": {
"is_group": 1,
"Gew\u00e4hrte Boni 7 % USt": { "Gew\u00e4hrte Boni 7 % USt": {
"account_number": "4750" "account_number": "4750"
}, },
@ -2838,8 +2852,6 @@
"account_number": "6398" "account_number": "6398"
} }
}, },
"Versicherungen (Gruppe)": {
"is_group": 1,
"Versicherungen": { "Versicherungen": {
"account_number": "6400" "account_number": "6400"
}, },
@ -2887,54 +2899,32 @@
}, },
"Mietleasing (bewegliche Wirtschaftsg\u00fcter)": { "Mietleasing (bewegliche Wirtschaftsg\u00fcter)": {
"account_number": "6498" "account_number": "6498"
}
}, },
"Fahrzeugkosten (Gruppe)": { "Fahrzeugkosten (Gruppe)": {
"is_group": 1, "is_group": 1,
"Fahrzeugkosten": { "Fahrzeugkosten": {
"account_number": "6500" "account_number": "6500"
}, },
"Kfz-Versicherungen (Gruppe)": {
"is_group": 1,
"Kfz-Versicherungen": { "Kfz-Versicherungen": {
"account_number": "6520" "account_number": "6520"
}
}, },
"Laufende Kfz-Betriebskosten (Gruppe)": {
"is_group": 1,
"Laufende Kfz-Betriebskosten": { "Laufende Kfz-Betriebskosten": {
"account_number": "6530" "account_number": "6530"
}
}, },
"Kfz-Reparaturen (Gruppe)": {
"is_group": 1,
"Kfz-Reparaturen": { "Kfz-Reparaturen": {
"account_number": "6540" "account_number": "6540"
}
}, },
"Garagenmiete (Gruppe)": {
"is_group": 1,
"Garagenmiete": { "Garagenmiete": {
"account_number": "6550" "account_number": "6550"
}
}, },
"Mietleasing Kfz (Gruppe)": {
"is_group": 1,
"Mietleasing Kfz": { "Mietleasing Kfz": {
"account_number": "6560" "account_number": "6560"
}
}, },
"Sonstige Kfz-Kosten (Gruppe)": {
"is_group": 1,
"Sonstige Kfz-Kosten": { "Sonstige Kfz-Kosten": {
"account_number": "6570" "account_number": "6570"
}
}, },
"Mautgeb\u00fchren (Gruppe)": {
"is_group": 1,
"Mautgeb\u00fchren": { "Mautgeb\u00fchren": {
"account_number": "6580" "account_number": "6580"
}
}, },
"Kfz-Kosten f. betrieblich genutzte zum Privatverm\u00f6gen geh\u00f6rende Kraftfahrzeuge": { "Kfz-Kosten f. betrieblich genutzte zum Privatverm\u00f6gen geh\u00f6rende Kraftfahrzeuge": {
"account_number": "6590" "account_number": "6590"
@ -2996,6 +2986,8 @@
"Nicht abzugsf\u00e4hige Betriebsausgaben aus Werbe- und Repr\u00e4sentationskosten": { "Nicht abzugsf\u00e4hige Betriebsausgaben aus Werbe- und Repr\u00e4sentationskosten": {
"account_number": "6645" "account_number": "6645"
}, },
"Reisekosten Arbeitnehmer (Gruppe)": {
"is_group": 1,
"Reisekosten Arbeitnehmer": { "Reisekosten Arbeitnehmer": {
"account_number": "6650" "account_number": "6650"
}, },
@ -3010,6 +3002,7 @@
}, },
"Kilometergelderstattung Arbeitnehmer": { "Kilometergelderstattung Arbeitnehmer": {
"account_number": "6668" "account_number": "6668"
}
}, },
"Reisekosten Unternehmer (Gruppe)": { "Reisekosten Unternehmer (Gruppe)": {
"is_group": 1, "is_group": 1,

View File

@ -48,12 +48,6 @@ frappe.ui.form.on('Accounting Dimension', {
frm.set_value('label', frm.doc.document_type); frm.set_value('label', frm.doc.document_type);
frm.set_value('fieldname', frappe.model.scrub(frm.doc.document_type)); frm.set_value('fieldname', frappe.model.scrub(frm.doc.document_type));
if (frm.is_new()){
let row = frappe.model.add_child(frm.doc, "Accounting Dimension Detail", "dimension_defaults");
row.reference_document = frm.doc.document_type;
frm.refresh_fields("dimension_defaults");
}
frappe.db.get_value('Accounting Dimension', {'document_type': frm.doc.document_type}, 'document_type', (r) => { frappe.db.get_value('Accounting Dimension', {'document_type': frm.doc.document_type}, 'document_type', (r) => {
if (r && r.document_type) { if (r && r.document_type) {
frm.set_df_property('document_type', 'description', "Document type is already set as dimension"); frm.set_df_property('document_type', 'description', "Document type is already set as dimension");

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "field:label", "autoname": "field:label",
"creation": "2019-05-04 18:13:37.002352", "creation": "2019-05-04 18:13:37.002352",
"doctype": "DocType", "doctype": "DocType",
@ -46,7 +47,8 @@
"options": "Accounting Dimension Detail" "options": "Accounting Dimension Detail"
} }
], ],
"modified": "2019-07-17 16:49:31.134385", "links": [],
"modified": "2020-03-22 20:34:39.805728",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounting Dimension", "name": "Accounting Dimension",
@ -63,9 +65,20 @@
"role": "System Manager", "role": "System Manager",
"share": 1, "share": 1,
"write": 1 "write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
} }
], ],
"quick_entry": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "ASC", "sort_order": "ASC",
"track_changes": 1 "track_changes": 1

View File

@ -172,7 +172,7 @@ def get_doctypes_with_dimensions():
return doclist return doclist
def get_accounting_dimensions(as_list=True): def get_accounting_dimensions(as_list=True):
accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled"]) accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled", "document_type"])
if as_list: if as_list:
return [d.fieldname for d in accounting_dimensions] return [d.fieldname for d in accounting_dimensions]
@ -186,6 +186,18 @@ def get_checks_for_pl_and_bs_accounts():
return dimensions return dimensions
def get_dimension_with_children(doctype, dimension):
if isinstance(dimension, list):
dimension = dimension[0]
all_dimensions = []
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
all_dimensions += [c.name for c in children]
return all_dimensions
@frappe.whitelist() @frappe.whitelist()
def get_dimension_filters(): def get_dimension_filters():
dimension_filters = frappe.db.sql(""" dimension_filters = frappe.db.sql("""

View File

@ -0,0 +1,8 @@
frappe.ui.form.on('Accounts Settings', {
refresh: function(frm) {
frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through");
frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods");
frm.set_df_property("credit_controller", "label", "Credit Manager");
}
});

View File

@ -7,7 +7,20 @@ frappe.ui.form.on('Bank', {
}, },
refresh: function(frm) { refresh: function(frm) {
add_fields_to_mapping_table(frm); add_fields_to_mapping_table(frm);
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank' };
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
if (frm.doc.__islocal) {
frm.set_df_property('address_and_contact', 'hidden', 1);
frappe.contacts.clear_address_and_contact(frm);
} }
else {
frm.set_df_property('address_and_contact', 'hidden', 0);
frappe.contacts.render_address_and_contact(frm);
}
},
}); });

View File

@ -1,224 +1,137 @@
{ {
"allow_copy": 0, "actions": [],
"allow_events_in_timeline": 0, "allow_import": 1,
"allow_guest_to_view": 0, "allow_rename": 1,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:bank_name", "autoname": "field:bank_name",
"beta": 0,
"creation": "2018-04-07 16:59:59.496668", "creation": "2018-04-07 16:59:59.496668",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "", "document_type": "Setup",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [
"bank_details_section",
"bank_name",
"swift_number",
"column_break_1",
"branch_code",
"website",
"address_and_contact",
"address_html",
"column_break_13",
"contact_html",
"data_import_configuration_section",
"bank_transaction_mapping",
"section_break_4",
"plaid_access_token"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_name", "fieldname": "bank_name",
"fieldtype": "Data", "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": "Bank Name", "label": "Bank Name",
"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": 1, "reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1 "unique": 1
}, },
{ {
"allow_bulk_edit": 0, "fieldname": "bank_details_section",
"allow_in_quick_entry": 0, "fieldtype": "Section Break",
"allow_on_submit": 0, "label": "Bank Details"
"bold": 0, },
{
"allow_in_quick_entry": 1,
"fieldname": "swift_number",
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "SWIFT number",
"unique": 1
},
{
"fieldname": "column_break_1",
"fieldtype": "Column Break",
"search_index": 1
},
{
"allow_in_quick_entry": 1,
"fieldname": "branch_code",
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Branch Code",
"unique": 1
},
{
"fieldname": "address_and_contact",
"fieldtype": "Section Break",
"label": "Address and Contact",
"options": "fa fa-map-marker"
},
{
"fieldname": "address_html",
"fieldtype": "HTML",
"label": "Address HTML"
},
{
"fieldname": "website",
"fieldtype": "Data",
"label": "Website"
},
{
"fieldname": "column_break_13",
"fieldtype": "Column Break"
},
{
"fieldname": "contact_html",
"fieldtype": "HTML",
"label": "Contact HTML"
},
{
"collapsible": 1, "collapsible": 1,
"columns": 0,
"fieldname": "data_import_configuration_section", "fieldname": "data_import_configuration_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Data Import Configuration"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Data Import Configuration",
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_transaction_mapping", "fieldname": "bank_transaction_mapping",
"fieldtype": "Table", "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": "Bank Transaction Mapping", "label": "Bank Transaction Mapping",
"length": 0, "options": "Bank Transaction Mapping"
"no_copy": 0,
"options": "Bank Transaction Mapping",
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_4", "fieldname": "section_break_4",
"fieldtype": "Section Break", "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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "plaid_access_token", "fieldname": "plaid_access_token",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "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": "Plaid Access Token", "label": "Plaid Access Token",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0, "read_only": 1
"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
} }
], ],
"has_web_view": 0, "links": [],
"hide_heading": 0, "modified": "2020-03-25 21:22:33.496264",
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-11-27 16:12:13.938776",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank", "name": "Bank",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "System Manager", "role": "System Manager",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 1, "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 1, "track_changes": 1
"track_seen": 0,
"track_views": 0
} }

View File

@ -5,6 +5,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document
from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
class Bank(Document): class Bank(Document):
pass def onload(self):
"""Load address and contacts in `__onload`"""
load_address_and_contact(self)
def on_trash(self):
delete_contact_and_address('Bank', self.name)

View File

@ -24,8 +24,6 @@
"iban", "iban",
"column_break_12", "column_break_12",
"bank_account_no", "bank_account_no",
"branch_code",
"swift_number",
"address_and_contact", "address_and_contact",
"address_html", "address_html",
"website", "website",
@ -145,17 +143,6 @@
"label": "Bank Account No", "label": "Bank Account No",
"length": 30 "length": 30
}, },
{
"fieldname": "branch_code",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Branch Code"
},
{
"fieldname": "swift_number",
"fieldtype": "Data",
"label": "SWIFT number"
},
{ {
"fieldname": "address_and_contact", "fieldname": "address_and_contact",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -213,7 +200,7 @@
} }
], ],
"links": [], "links": [],
"modified": "2020-01-29 20:42:26.458316", "modified": "2020-01-30 20:42:26.458316",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Account", "name": "Bank Account",

View File

@ -4,8 +4,8 @@
cur_frm.add_fetch('bank_account','account','account'); cur_frm.add_fetch('bank_account','account','account');
cur_frm.add_fetch('bank_account','bank_account_no','bank_account_no'); cur_frm.add_fetch('bank_account','bank_account_no','bank_account_no');
cur_frm.add_fetch('bank_account','iban','iban'); cur_frm.add_fetch('bank_account','iban','iban');
cur_frm.add_fetch('bank_account','branch_code','branch_code'); cur_frm.add_fetch('bank','branch_code','branch_code');
cur_frm.add_fetch('bank_account','swift_number','swift_number'); cur_frm.add_fetch('bank','swift_number','swift_number');
frappe.ui.form.on('Bank Guarantee', { frappe.ui.form.on('Bank Guarantee', {
setup: function(frm) { setup: function(frm) {

View File

@ -6,6 +6,7 @@ from __future__ import unicode_literals
import frappe, json import frappe, json
from frappe.model.document import Document from frappe.model.document import Document
from frappe import _ from frappe import _
from frappe.desk.search import sanitize_searchfield
class BankGuarantee(Document): class BankGuarantee(Document):
def validate(self): def validate(self):
@ -22,5 +23,8 @@ class BankGuarantee(Document):
@frappe.whitelist() @frappe.whitelist()
def get_vouchar_detials(column_list, doctype, docname): def get_vouchar_detials(column_list, doctype, docname):
column_list = json.loads(column_list)
for col in column_list:
sanitize_searchfield(col)
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s''' return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0] .format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]

View File

@ -32,10 +32,12 @@ frappe.ui.form.on('C-Form Invoice Detail', {
invoice_no(frm, cdt, cdn) { invoice_no(frm, cdt, cdn) {
let d = frappe.get_doc(cdt, cdn); let d = frappe.get_doc(cdt, cdn);
if (d.invoice_no) {
frm.call('get_invoice_details', { frm.call('get_invoice_details', {
invoice_no: d.invoice_no invoice_no: d.invoice_no
}).then(r => { }).then(r => {
frappe.model.set_value(cdt, cdn, r.message); frappe.model.set_value(cdt, cdn, r.message);
}); });
} }
}
}); });

View File

@ -124,11 +124,13 @@
], ],
"icon": "fa fa-money", "icon": "fa fa-money",
"idx": 1, "idx": 1,
"is_tree": 1,
"links": [], "links": [],
"modified": "2020-01-28 13:50:23.430434", "modified": "2020-03-18 17:59:04.321637",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Cost Center", "name": "Cost Center",
"nsm_parent_field": "parent_cost_center",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@ -30,18 +30,10 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
frm.doc.accounts.forEach(d=> { frm.doc.accounts.forEach(d=> {
total_amt = total_amt + d['new_balance_in_base_currency']; total_amt = total_amt + d['new_balance_in_base_currency'];
}); });
if(total_amt === r.sum) { if(total_amt !== r.sum) {
frm.add_custom_button(__("Journal Entry"), function(){ frm.add_custom_button(__('Journal Entry'), function() {
frappe.route_options = {
'reference_type': 'Exchange Rate Revaluation',
'reference_name': frm.doc.name
};
frappe.set_route("List", "Journal Entry");
}, __("View"));
} else {
frm.add_custom_button(__('Create Journal Entry'), function() {
return frm.events.make_jv(frm); return frm.events.make_jv(frm);
}); }, __('Create'));
} }
}, 'Journal Entry'); }, 'Journal Entry');
} }

View File

@ -0,0 +1,11 @@
from __future__ import unicode_literals
def get_data():
return {
'fieldname': 'reference_name',
'transactions': [
{
'items': ['Journal Entry']
}
]
}

View File

@ -114,13 +114,13 @@
"fieldname": "debit_in_account_currency", "fieldname": "debit_in_account_currency",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Debit Amount in Account Currency", "label": "Debit Amount in Account Currency",
"options": "currency" "options": "account_currency"
}, },
{ {
"fieldname": "credit_in_account_currency", "fieldname": "credit_in_account_currency",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Credit Amount in Account Currency", "label": "Credit Amount in Account Currency",
"options": "currency" "options": "account_currency"
}, },
{ {
"fieldname": "against", "fieldname": "against",
@ -250,7 +250,7 @@
"icon": "fa fa-list", "icon": "fa fa-list",
"idx": 1, "idx": 1,
"in_create": 1, "in_create": 1,
"modified": "2020-02-10 04:54:57.777905", "modified": "2020-03-28 16:22:33.766994",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "GL Entry", "name": "GL Entry",

View File

@ -233,35 +233,14 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))) frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]: if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal)
def update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal):
data = []
# Update outstanding amt on against voucher
if against_voucher_type == "Fees":
ref_doc = frappe.get_doc(against_voucher_type, against_voucher) ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
ref_doc.db_set('outstanding_amount', bal)
ref_doc.set_status(update=True)
return
elif against_voucher_type == "Purchase Invoice":
from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_status
data = frappe.db.get_value(against_voucher_type, against_voucher,
["name as purchase_invoice", "outstanding_amount",
"is_return", "due_date", "docstatus"])
elif against_voucher_type == "Sales Invoice":
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_status
data = frappe.db.get_value(against_voucher_type, against_voucher,
["name as sales_invoice", "outstanding_amount", "is_discounted",
"is_return", "due_date", "docstatus"])
precision = frappe.get_precision(against_voucher_type, "outstanding_amount") # Didn't use db_set for optimisation purpose
data = list(data) ref_doc.outstanding_amount = bal
data.append(precision) frappe.db.set_value(against_voucher_type, against_voucher, 'outstanding_amount', bal)
status = get_status(data)
frappe.db.set_value(against_voucher_type, against_voucher, { ref_doc.set_status(update=True)
'outstanding_amount': bal,
'status': status
})
def validate_frozen_account(account, adv_adj=None): def validate_frozen_account(account, adv_adj=None):
frozen_account = frappe.db.get_value("Account", account, "freeze_account") frozen_account = frappe.db.get_value("Account", account, "freeze_account")

View File

@ -38,6 +38,7 @@ class TestGLEntry(unittest.TestCase):
filters={"voucher_type": "Journal Entry", "voucher_no": je.name}, filters={"voucher_type": "Journal Entry", "voucher_no": je.name},
order_by="creation" order_by="creation"
) )
self.assertTrue(all(entry.to_rename == 1 for entry in gl_entries)) self.assertTrue(all(entry.to_rename == 1 for entry in gl_entries))
old_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0] old_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0]

View File

@ -454,8 +454,10 @@ class JournalEntry(AccountsController):
def set_print_format_fields(self): def set_print_format_fields(self):
bank_amount = party_amount = total_amount = 0.0 bank_amount = party_amount = total_amount = 0.0
currency = bank_account_currency = party_account_currency = pay_to_recd_from= None currency = bank_account_currency = party_account_currency = pay_to_recd_from= None
party_type = None
for d in self.get('accounts'): for d in self.get('accounts'):
if d.party_type in ['Customer', 'Supplier'] and d.party: if d.party_type in ['Customer', 'Supplier'] and d.party:
party_type = d.party_type
if not pay_to_recd_from: if not pay_to_recd_from:
pay_to_recd_from = d.party pay_to_recd_from = d.party
@ -467,9 +469,9 @@ class JournalEntry(AccountsController):
bank_amount += (d.debit_in_account_currency or d.credit_in_account_currency) bank_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
bank_account_currency = d.account_currency bank_account_currency = d.account_currency
if pay_to_recd_from: if party_type and pay_to_recd_from:
self.pay_to_recd_from = frappe.db.get_value(d.party_type, pay_to_recd_from, self.pay_to_recd_from = frappe.db.get_value(party_type, pay_to_recd_from,
"customer_name" if d.party_type=="Customer" else "supplier_name") "customer_name" if party_type=="Customer" else "supplier_name")
if bank_amount: if bank_amount:
total_amount = bank_amount total_amount = bank_amount
currency = bank_account_currency currency = bank_account_currency

View File

@ -33,7 +33,9 @@ frappe.ui.form.on('Payment Entry', {
frm.set_query("party_bank_account", function() { frm.set_query("party_bank_account", function() {
return { return {
filters: { filters: {
"is_company_account":0 "is_company_account":0,
party_type: frm.doc.party_type,
party: frm.doc.party
} }
} }
}); });

View File

@ -161,15 +161,15 @@ class TestPaymentEntry(unittest.TestCase):
pe.insert() pe.insert()
pe.submit() pe.submit()
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
self.assertEqual(outstanding_amount, 0) self.assertEqual(flt(outstanding_amount), 0)
self.assertEqual(si.status, 'Paid') self.assertEqual(status, 'Paid')
pe.cancel() pe.cancel()
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
self.assertEqual(outstanding_amount, 100) self.assertEqual(flt(outstanding_amount), 100)
self.assertEqual(si.status, 'Unpaid') self.assertEqual(status, 'Unpaid')
def test_payment_against_purchase_invoice_to_check_status(self): def test_payment_against_purchase_invoice_to_check_status(self):
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC", pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
@ -182,15 +182,15 @@ class TestPaymentEntry(unittest.TestCase):
pe.insert() pe.insert()
pe.submit() pe.submit()
outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
self.assertEqual(outstanding_amount, 0) self.assertEqual(flt(outstanding_amount), 0)
self.assertEqual(pi.status, 'Paid') self.assertEqual(status, 'Paid')
pe.cancel() pe.cancel()
outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
self.assertEqual(outstanding_amount, 100) self.assertEqual(flt(outstanding_amount), 250)
self.assertEqual(pi.status, 'Unpaid') self.assertEqual(status, 'Unpaid')
def test_payment_entry_against_ec(self): def test_payment_entry_against_ec(self):

View File

@ -839,7 +839,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_from": "bank_account.branch_code", "fetch_from": "bank.branch_code",
"fieldname": "branch_code", "fieldname": "branch_code",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "hidden": 0,
@ -873,7 +873,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_from": "bank_account.swift_number", "fetch_from": "bank.swift_number",
"fieldname": "swift_number", "fieldname": "swift_number",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "hidden": 0,

View File

@ -317,13 +317,13 @@ def make_payment_request(**args):
"payment_request_type": args.get("payment_request_type"), "payment_request_type": args.get("payment_request_type"),
"currency": ref_doc.currency, "currency": ref_doc.currency,
"grand_total": grand_total, "grand_total": grand_total,
"email_to": args.recipient_id or "", "email_to": args.recipient_id or ref_doc.owner,
"subject": _("Payment Request for {0}").format(args.dn), "subject": _("Payment Request for {0}").format(args.dn),
"message": gateway_account.get("message") or get_dummy_message(ref_doc), "message": gateway_account.get("message") or get_dummy_message(ref_doc),
"reference_doctype": args.dt, "reference_doctype": args.dt,
"reference_name": args.dn, "reference_name": args.dn,
"party_type": args.get("party_type"), "party_type": args.get("party_type") or "Customer",
"party": args.get("party"), "party": args.get("party") or ref_doc.customer,
"bank_account": bank_account "bank_account": bank_account
}) })

View File

@ -125,27 +125,6 @@ class PurchaseInvoice(BuyingController):
else: else:
self.remarks = _("No Remarks") self.remarks = _("No Remarks")
def set_status(self, update=False, status=None, update_modified=True):
if self.is_new():
if self.get('amended_from'):
self.status = 'Draft'
return
if not status:
precision = self.precision("outstanding_amount")
args = [
self.name,
self.outstanding_amount,
self.is_return,
self.due_date,
self.docstatus,
precision
]
self.status = get_status(args)
if update:
self.db_set('status', self.status, update_modified = update_modified)
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
if not self.credit_to: if not self.credit_to:
self.credit_to = get_party_account("Supplier", self.supplier, self.company) self.credit_to = get_party_account("Supplier", self.supplier, self.company)
@ -167,10 +146,14 @@ class PurchaseInvoice(BuyingController):
["account_type", "report_type", "account_currency"], as_dict=True) ["account_type", "report_type", "account_currency"], as_dict=True)
if account.report_type != "Balance Sheet": if account.report_type != "Balance Sheet":
frappe.throw(_("Credit To account must be a Balance Sheet account")) frappe.throw(_("Please ensure {} account is a Balance Sheet account. \
You can change the parent account to a Balance Sheet account or select a different account.")
.format(frappe.bold("Credit To")), title=_("Invalid Account"))
if self.supplier and account.account_type != "Payable": if self.supplier and account.account_type != "Payable":
frappe.throw(_("Credit To account must be a Payable account")) frappe.throw(_("Please ensure {} account is a Payable account. \
Change the account type to Payable or select a different account.")
.format(frappe.bold("Credit To")), title=_("Invalid Account"))
self.party_account_currency = account.account_currency self.party_account_currency = account.account_currency
@ -1028,34 +1011,6 @@ class PurchaseInvoice(BuyingController):
# calculate totals again after applying TDS # calculate totals again after applying TDS
self.calculate_taxes_and_totals() self.calculate_taxes_and_totals()
def get_status(*args):
purchase_invoice, outstanding_amount, is_return, due_date, docstatus, precision = args[0]
outstanding_amount = flt(outstanding_amount, precision)
due_date = getdate(due_date)
now_date = getdate()
if docstatus == 2:
status = "Cancelled"
elif docstatus == 1:
if outstanding_amount > 0 and due_date < now_date:
status = "Overdue"
elif outstanding_amount > 0 and due_date >= now_date:
status = "Unpaid"
#Check if outstanding amount is 0 due to debit note issued against invoice
elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': purchase_invoice, 'docstatus': 1}):
status = "Debit Note Issued"
elif is_return == 1:
status = "Return"
elif outstanding_amount <=0:
status = "Paid"
else:
status = "Submitted"
else:
status = "Draft"
return status
def get_list_context(context=None): def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context) list_context = get_list_context(context)
@ -1116,3 +1071,6 @@ def block_invoice(name, release_date, hold_comment=None):
def make_inter_company_sales_invoice(source_name, target_doc=None): def make_inter_company_sales_invoice(source_name, target_doc=None):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
return make_inter_company_transaction("Purchase Invoice", source_name, target_doc) return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
def on_doctype_update():
frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"])

View File

@ -761,7 +761,7 @@
"depends_on": "is_fixed_asset", "depends_on": "is_fixed_asset",
"fetch_from": "item_code.asset_category", "fetch_from": "item_code.asset_category",
"fieldname": "asset_category", "fieldname": "asset_category",
"fieldtype": "Data", "fieldtype": "Link",
"label": "Asset Category", "label": "Asset Category",
"options": "Asset Category", "options": "Asset Category",
"read_only": 1 "read_only": 1
@ -777,7 +777,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-03-05 14:20:17.297284", "modified": "2020-04-01 14:20:17.297284",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Item", "name": "Purchase Invoice Item",

View File

@ -423,7 +423,10 @@ def make_invoice(pos_profile, doc_list={}, email_queue_list={}, customers_list={
name_list.append(name) name_list.append(name)
email_queue = make_email_queue(email_queue_list) email_queue = make_email_queue(email_queue_list)
if isinstance(pos_profile, string_types):
pos_profile = json.loads(pos_profile) pos_profile = json.loads(pos_profile)
customers = get_customers_list(pos_profile) customers = get_customers_list(pos_profile)
return { return {
'invoice': name_list, 'invoice': name_list,

View File

@ -437,13 +437,17 @@ class SalesInvoice(SellingController):
if (not for_validate) or (for_validate and not self.get(fieldname)): if (not for_validate) or (for_validate and not self.get(fieldname)):
self.set(fieldname, pos.get(fieldname)) self.set(fieldname, pos.get(fieldname))
customer_price_list = frappe.get_value("Customer", self.customer, 'default_price_list')
if pos.get("company_address"): if pos.get("company_address"):
self.company_address = pos.get("company_address") self.company_address = pos.get("company_address")
if not customer_price_list: customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group'])
self.set('selling_price_list', pos.get('selling_price_list'))
customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list')
selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list')
if selling_price_list:
self.set('selling_price_list', selling_price_list)
if not for_validate: if not for_validate:
self.update_stock = cint(pos.get("update_stock")) self.update_stock = cint(pos.get("update_stock"))
@ -474,13 +478,17 @@ class SalesInvoice(SellingController):
["account_type", "report_type", "account_currency"], as_dict=True) ["account_type", "report_type", "account_currency"], as_dict=True)
if not account: if not account:
frappe.throw(_("Debit To is required")) frappe.throw(_("Debit To is required"), title=_("Account Missing"))
if account.report_type != "Balance Sheet": if account.report_type != "Balance Sheet":
frappe.throw(_("Debit To account must be a Balance Sheet account")) frappe.throw(_("Please ensure {} account is a Balance Sheet account. \
You can change the parent account to a Balance Sheet account or select a different account.")
.format(frappe.bold("Debit To")), title=_("Invalid Account"))
if self.customer and account.account_type != "Receivable": if self.customer and account.account_type != "Receivable":
frappe.throw(_("Debit To account must be a Receivable account")) frappe.throw(_("Please ensure {} account is a Receivable account. \
Change the account type to Receivable or select a different account.")
.format(frappe.bold("Debit To")), title=_("Invalid Account"))
self.party_account_currency = account.account_currency self.party_account_currency = account.account_currency
@ -1223,18 +1231,38 @@ class SalesInvoice(SellingController):
self.status = 'Draft' self.status = 'Draft'
return return
if not status:
precision = self.precision("outstanding_amount") precision = self.precision("outstanding_amount")
args = [ outstanding_amount = flt(self.outstanding_amount, precision)
self.name, due_date = getdate(self.due_date)
self.outstanding_amount, nowdate = getdate()
self.is_discounted,
self.is_return, discounting_status = None
self.due_date, if self.is_discounted:
self.docstatus, discountng_status = get_discounting_status(self.name)
precision,
] if not status:
self.status = get_status(args) if self.docstatus == 2:
status = "Cancelled"
elif self.docstatus == 1:
if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
self.status = "Overdue and Discounted"
elif outstanding_amount > 0 and due_date < nowdate:
self.status = "Overdue"
elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed':
self.status = "Unpaid and Discounted"
elif outstanding_amount > 0 and due_date >= nowdate:
self.status = "Unpaid"
#Check if outstanding amount is 0 due to credit note issued against invoice
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
self.status = "Credit Note Issued"
elif self.is_return == 1:
self.status = "Return"
elif outstanding_amount<=0:
self.status = "Paid"
else:
self.status = "Submitted"
else:
self.status = "Draft"
if update: if update:
self.db_set('status', self.status, update_modified = update_modified) self.db_set('status', self.status, update_modified = update_modified)
@ -1259,42 +1287,6 @@ def get_discounting_status(sales_invoice):
return status return status
def get_status(*args):
sales_invoice, outstanding_amount, is_discounted, is_return, due_date, docstatus, precision = args[0]
discounting_status = None
if is_discounted:
discounting_status = get_discounting_status(sales_invoice)
outstanding_amount = flt(outstanding_amount, precision)
due_date = getdate(due_date)
now_date = getdate()
if docstatus == 2:
status = "Cancelled"
elif docstatus == 1:
if outstanding_amount > 0 and due_date < now_date and is_discounted and discounting_status=='Disbursed':
status = "Overdue and Discounted"
elif outstanding_amount > 0 and due_date < now_date:
status = "Overdue"
elif outstanding_amount > 0 and due_date >= now_date and is_discounted and discounting_status=='Disbursed':
status = "Unpaid and Discounted"
elif outstanding_amount > 0 and due_date >= now_date:
status = "Unpaid"
#Check if outstanding amount is 0 due to credit note issued against invoice
elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': sales_invoice, 'docstatus': 1}):
status = "Credit Note Issued"
elif is_return == 1:
status = "Return"
elif outstanding_amount <=0:
status = "Paid"
else:
status = "Submitted"
else:
status = "Draft"
return status
def validate_inter_company_party(doctype, party, company, inter_company_reference): def validate_inter_company_party(doctype, party, company, inter_company_reference):
if not party: if not party:
return return

View File

@ -786,7 +786,7 @@ class TestSalesInvoice(unittest.TestCase):
def test_make_pos_invoice(self): def test_make_pos_invoice(self):
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
make_pos_profile() pos_profile = make_pos_profile()
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1") pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True) pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
@ -802,7 +802,7 @@ class TestSalesInvoice(unittest.TestCase):
pos.append("taxes", tax) pos.append("taxes", tax)
invoice_data = [{'09052016142': pos}] invoice_data = [{'09052016142': pos}]
si = make_invoice(invoice_data).get('invoice') si = make_invoice(pos_profile, invoice_data).get('invoice')
self.assertEqual(si[0], '09052016142') self.assertEqual(si[0], '09052016142')
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1}) sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1})
@ -820,7 +820,7 @@ class TestSalesInvoice(unittest.TestCase):
if allow_negative_stock: if allow_negative_stock:
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0) frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
make_pos_profile() pos_profile = make_pos_profile()
timestamp = cint(time.time()) timestamp = cint(time.time())
item = make_item("_Test POS Item") item = make_item("_Test POS Item")
@ -834,7 +834,7 @@ class TestSalesInvoice(unittest.TestCase):
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}] {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}]
invoice_data = [{timestamp: pos}] invoice_data = [{timestamp: pos}]
si = make_invoice(invoice_data).get('invoice') si = make_invoice(pos_profile, invoice_data).get('invoice')
self.assertEqual(si[0], timestamp) self.assertEqual(si[0], timestamp)
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp}) sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
@ -843,7 +843,7 @@ class TestSalesInvoice(unittest.TestCase):
timestamp = cint(time.time()) timestamp = cint(time.time())
pos["offline_pos_name"] = timestamp pos["offline_pos_name"] = timestamp
invoice_data = [{timestamp: pos}] invoice_data = [{timestamp: pos}]
si1 = make_invoice(invoice_data).get('invoice') si1 = make_invoice(pos_profile, invoice_data).get('invoice')
self.assertEqual(si1[0], timestamp) self.assertEqual(si1[0], timestamp)
sales_invoice1 = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp}) sales_invoice1 = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})

View File

@ -136,12 +136,11 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
def make_entry(args, adv_adj, update_outstanding, from_repost=False): def make_entry(args, adv_adj, update_outstanding, from_repost=False):
args.update({"doctype": "GL Entry"}) gle = frappe.new_doc("GL Entry")
gle = frappe.get_doc(args) gle.update(args)
gle.flags.ignore_permissions = 1 gle.flags.ignore_permissions = 1
gle.flags.from_repost = from_repost gle.flags.from_repost = from_repost
gle.validate() gle.validate()
gle.flags.ignore_permissions = True
gle.db_insert() gle.db_insert()
gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
gle.flags.ignore_validate = True gle.flags.ignore_validate = True

View File

@ -11,7 +11,7 @@ from frappe.utils import (add_days, getdate, formatdate, date_diff,
add_years, get_timestamp, nowdate, flt, cstr, add_months, get_last_day) add_years, get_timestamp, nowdate, flt, cstr, add_months, get_last_day)
from frappe.contacts.doctype.address.address import (get_address_display, from frappe.contacts.doctype.address.address import (get_address_display,
get_default_address, get_company_address) get_default_address, get_company_address)
from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact from frappe.contacts.doctype.contact.contact import get_contact_details
from erpnext.exceptions import PartyFrozen, PartyDisabled, InvalidAccountCurrency from erpnext.exceptions import PartyFrozen, PartyDisabled, InvalidAccountCurrency
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext import get_company_currency from erpnext import get_company_currency
@ -281,8 +281,8 @@ def validate_party_gle_currency(party_type, party, company, party_account_curren
existing_gle_currency = get_party_gle_currency(party_type, party, company) existing_gle_currency = get_party_gle_currency(party_type, party, company)
if existing_gle_currency and party_account_currency != existing_gle_currency: if existing_gle_currency and party_account_currency != existing_gle_currency:
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}") frappe.throw(_("{0} {1} has accounting entries in currency {2} for company {3}. Please select a receivable or payable account with currency {2}.")
.format(party_type, party, existing_gle_currency), InvalidAccountCurrency) .format(frappe.bold(party_type), frappe.bold(party), frappe.bold(existing_gle_currency), frappe.bold(company)), InvalidAccountCurrency)
def validate_party_accounts(doc): def validate_party_accounts(doc):
companies = [] companies = []
@ -295,15 +295,13 @@ def validate_party_accounts(doc):
companies.append(account.company) companies.append(account.company)
party_account_currency = frappe.db.get_value("Account", account.account, "account_currency", cache=True) party_account_currency = frappe.db.get_value("Account", account.account, "account_currency", cache=True)
existing_gle_currency = get_party_gle_currency(doc.doctype, doc.name, account.company)
if frappe.db.get_default("Company"): if frappe.db.get_default("Company"):
company_default_currency = frappe.get_cached_value('Company', company_default_currency = frappe.get_cached_value('Company',
frappe.db.get_default("Company"), "default_currency") frappe.db.get_default("Company"), "default_currency")
else: else:
company_default_currency = frappe.db.get_value('Company', account.company, "default_currency") company_default_currency = frappe.db.get_value('Company', account.company, "default_currency")
if existing_gle_currency and party_account_currency != existing_gle_currency: validate_party_gle_currency(doc.doctype, doc.name, account.company, party_account_currency)
frappe.throw(_("Accounting entries have already been made in currency {0} for company {1}. Please select a receivable or payable account with currency {0}.").format(existing_gle_currency, account.company))
if doc.get("default_currency") and party_account_currency and company_default_currency: if doc.get("default_currency") and party_account_currency and company_default_currency:
if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency: if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency:
@ -615,3 +613,26 @@ def get_partywise_advanced_payment_amount(party_type, posting_date = None):
if data: if data:
return frappe._dict(data) return frappe._dict(data)
def get_default_contact(doctype, name):
"""
Returns default contact for the given doctype and name.
Can be ordered by `contact_type` to either is_primary_contact or is_billing_contact.
"""
out = frappe.db.sql("""
SELECT dl.parent, c.is_primary_contact, c.is_billing_contact
FROM `tabDynamic Link` dl
INNER JOIN tabContact c ON c.name = dl.parent
WHERE
dl.link_doctype=%s AND
dl.link_name=%s AND
dl.parenttype = "Contact"
ORDER BY is_primary_contact DESC, is_billing_contact DESC
""", (doctype, name))
if out:
try:
return out[0][0]
except:
return None
else:
return None

View File

@ -218,15 +218,15 @@
<td></td> <td></td>
<td style="text-align: right"><b>{%= __("Total") %}</b></td> <td style="text-align: right"><b>{%= __("Total") %}</b></td>
<td style="text-align: right"> <td style="text-align: right">
{%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %}</td> {%= format_currency(data[i]["invoiced"], data[0]["currency"] ) %}</td>
{% if(!filters.show_future_payments) { %} {% if(!filters.show_future_payments) { %}
<td style="text-align: right"> <td style="text-align: right">
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td> {%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} </td> <td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %} </td>
{% } %} {% } %}
<td style="text-align: right"> <td style="text-align: right">
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td> {%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
{% if(filters.show_future_payments) { %} {% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %} {% if(report.report_name === "Accounts Receivable") { %}
@ -234,8 +234,8 @@
{%= data[i]["po_no"] %}</td> {%= data[i]["po_no"] %}</td>
{% } %} {% } %}
<td style="text-align: right">{%= data[i]["future_ref"] %}</td> <td style="text-align: right">{%= data[i]["future_ref"] %}</td>
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td> <td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[0]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td> <td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[0]["currency"]) %}</td>
{% } %} {% } %}
{% } %} {% } %}
{% } else { %} {% } else { %}
@ -256,10 +256,10 @@
{% } else { %} {% } else { %}
<td><b>{%= __("Total") %}</b></td> <td><b>{%= __("Total") %}</b></td>
{% } %} {% } %}
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td> <td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[0]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td> <td style="text-align: right">{%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td> <td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td> <td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
{% } %} {% } %}
{% } %} {% } %}
</tr> </tr>

View File

@ -7,7 +7,7 @@ from frappe import _, scrub
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr, now, time_diff_in_seconds from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr, now, time_diff_in_seconds
from collections import OrderedDict from collections import OrderedDict
from erpnext.accounts.utils import get_currency_precision from erpnext.accounts.utils import get_currency_precision
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
# This report gives a summary of all Outstanding Invoices considering the following # This report gives a summary of all Outstanding Invoices considering the following
@ -603,7 +603,6 @@ class ReceivablePayableReport(object):
self.add_supplier_filters(conditions, values) self.add_supplier_filters(conditions, values)
self.add_accounting_dimensions_filters(conditions, values) self.add_accounting_dimensions_filters(conditions, values)
return " and ".join(conditions), values return " and ".join(conditions), values
def get_order_by_condition(self): def get_order_by_condition(self):
@ -666,13 +665,16 @@ class ReceivablePayableReport(object):
doctype=doctype, lft=lft, rgt=rgt, key=key) doctype=doctype, lft=lft, rgt=rgt, key=key)
def add_accounting_dimensions_filters(self, conditions, values): def add_accounting_dimensions_filters(self, conditions, values):
accounting_dimensions = get_accounting_dimensions() accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions: if accounting_dimensions:
for dimension in accounting_dimensions: for dimension in accounting_dimensions:
if self.filters.get(dimension): if self.filters.get(dimension.fieldname):
conditions.append("{0} = %s".format(dimension)) if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
values.append(self.filters.get(dimension)) self.filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
self.filters.get(dimension.fieldname))
conditions.append("{0} in %s".format(dimension.fieldname))
values.append(tuple(self.filters.get(dimension.fieldname)))
def get_gle_balance(self, gle): def get_gle_balance(self, gle):
# get the balance of the GL (debit - credit) or reverse balance based on report type # get the balance of the GL (debit - credit) or reverse balance based on report type

View File

@ -16,7 +16,7 @@ from frappe import _
from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate, cstr) from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate, cstr)
from six import itervalues from six import itervalues
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False, def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False,
company=None, reset_period_on_fy_change=True): company=None, reset_period_on_fy_change=True):
@ -389,7 +389,7 @@ def set_gl_entries_by_account(
def get_additional_conditions(from_date, ignore_closing_entries, filters): def get_additional_conditions(from_date, ignore_closing_entries, filters):
additional_conditions = [] additional_conditions = []
accounting_dimensions = get_accounting_dimensions() accounting_dimensions = get_accounting_dimensions(as_list=False)
if ignore_closing_entries: if ignore_closing_entries:
additional_conditions.append("ifnull(voucher_type, '')!='Period Closing Voucher'") additional_conditions.append("ifnull(voucher_type, '')!='Period Closing Voucher'")
@ -415,8 +415,11 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
if accounting_dimensions: if accounting_dimensions:
for dimension in accounting_dimensions: for dimension in accounting_dimensions:
if filters.get(dimension): if filters.get(dimension.fieldname):
additional_conditions.append("{0} in (%({0})s)".format(dimension)) if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname))
additional_conditions.append("{0} in %({0})s".format(dimension.fieldname))
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""

View File

@ -2,7 +2,7 @@
<h4 class="text-center"> <h4 class="text-center">
{% if (filters.party_name) { %} {% if (filters.party_name) { %}
{%= filters.party_name %} {%= filters.party_name %}
{% } else if (filters.party && filters.show_name) { %} {% } else if (filters.party) { %}
{%= filters.party %} {%= filters.party %}
{% } else if (filters.account) { %} {% } else if (filters.account) { %}
{%= filters.account %} {%= filters.account %}

View File

@ -10,7 +10,7 @@ from frappe import _, _dict
from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.report.financial_statements import get_cost_centers_with_children from erpnext.accounts.report.financial_statements import get_cost_centers_with_children
from six import iteritems from six import iteritems
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
from collections import OrderedDict from collections import OrderedDict
def execute(filters=None): def execute(filters=None):
@ -131,7 +131,7 @@ def get_gl_entries(filters):
gl_entries = frappe.db.sql( gl_entries = frappe.db.sql(
""" """
select select
posting_date, account, party_type, party, name as gl_entry, posting_date, account, party_type, party,
voucher_type, voucher_no, cost_center, project, voucher_type, voucher_no, cost_center, project,
against_voucher_type, against_voucher, account_currency, against_voucher_type, against_voucher, account_currency,
remarks, against, is_opening {select_fields} remarks, against, is_opening {select_fields}
@ -194,12 +194,15 @@ def get_conditions(filters):
if match_conditions: if match_conditions:
conditions.append(match_conditions) conditions.append(match_conditions)
accounting_dimensions = get_accounting_dimensions() accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions: if accounting_dimensions:
for dimension in accounting_dimensions: for dimension in accounting_dimensions:
if filters.get(dimension): if filters.get(dimension.fieldname):
conditions.append("{0} in (%({0})s)".format(dimension)) if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname))
conditions.append("{0} in %({0})s".format(dimension.fieldname))
return "and {}".format(" and ".join(conditions)) if conditions else "" return "and {}".format(" and ".join(conditions)) if conditions else ""
@ -359,6 +362,12 @@ def get_columns(filters):
currency = get_company_currency(company) currency = get_company_currency(company)
columns = [ columns = [
{
"fieldname": "gl_entry",
"fieldtype": "Link",
"options": "GL Entry",
"hidden": 1
},
{ {
"label": _("Posting Date"), "label": _("Posting Date"),
"fieldname": "posting_date", "fieldname": "posting_date",

View File

@ -201,7 +201,8 @@ def get_columns(additional_table_columns, filters):
{ {
'label': _('Mode Of Payment'), 'label': _('Mode Of Payment'),
'fieldname': 'mode_of_payment', 'fieldname': 'mode_of_payment',
'fieldtype': 'Data', 'fieldtype': 'Link',
'options': 'Mode of Payment',
'width': 120 'width': 120
}, },
{ {
@ -309,6 +310,8 @@ def get_items(filters, additional_query_columns):
if additional_query_columns: if additional_query_columns:
additional_query_columns = ', ' + ', '.join(additional_query_columns) additional_query_columns = ', ' + ', '.join(additional_query_columns)
else:
additional_query_columns = ''
return frappe.db.sql(""" return frappe.db.sql("""
select select
@ -320,7 +323,7 @@ def get_items(filters, additional_query_columns):
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`, `tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`, `tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
`tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_amount`, `tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_amount`,
`tabPurchase Invoice`.supplier_name, `tabPurchase Invoice`.mode_of_payment {0} `tabPurchase Invoice`.`supplier_name`, `tabPurchase Invoice`.`mode_of_payment` {0}
from `tabPurchase Invoice`, `tabPurchase Invoice Item` from `tabPurchase Invoice`, `tabPurchase Invoice Item`
where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
`tabPurchase Invoice`.docstatus = 1 %s `tabPurchase Invoice`.docstatus = 1 %s

View File

@ -373,6 +373,8 @@ def get_items(filters, additional_query_columns):
if additional_query_columns: if additional_query_columns:
additional_query_columns = ', ' + ', '.join(additional_query_columns) additional_query_columns = ', ' + ', '.join(additional_query_columns)
else:
additional_query_columns = ''
return frappe.db.sql(""" return frappe.db.sql("""
select select

View File

@ -6,7 +6,7 @@ import frappe
from frappe.utils import flt from frappe.utils import flt
from frappe import msgprint, _ from frappe import msgprint, _
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
def execute(filters=None): def execute(filters=None):
return _execute(filters) return _execute(filters)
@ -341,14 +341,18 @@ def get_conditions(filters):
where parent=`tabSales Invoice`.name where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)""" and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
accounting_dimensions = get_accounting_dimensions() accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions: if accounting_dimensions:
for dimension in accounting_dimensions: for dimension in accounting_dimensions:
if filters.get(dimension): if filters.get(dimension.fieldname):
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname))
conditions += """ and exists(select name from `tabSales Invoice Item` conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.{0}, '') = %({0})s)""".format(dimension) and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)""".format(dimension.fieldname)
return conditions return conditions

View File

@ -7,7 +7,7 @@ from frappe import _
from frappe.utils import flt, getdate, formatdate, cstr from frappe.utils import flt, getdate, formatdate, cstr
from erpnext.accounts.report.financial_statements \ from erpnext.accounts.report.financial_statements \
import filter_accounts, set_gl_entries_by_account, filter_out_zero_value_rows import filter_accounts, set_gl_entries_by_account, filter_out_zero_value_rows
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit") value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit")
@ -109,7 +109,7 @@ def get_rootwise_opening_balances(filters, report_type):
additional_conditions += fb_conditions additional_conditions += fb_conditions
accounting_dimensions = get_accounting_dimensions() accounting_dimensions = get_accounting_dimensions(as_list=False)
query_filters = { query_filters = {
"company": filters.company, "company": filters.company,
@ -122,11 +122,14 @@ def get_rootwise_opening_balances(filters, report_type):
if accounting_dimensions: if accounting_dimensions:
for dimension in accounting_dimensions: for dimension in accounting_dimensions:
if filters.get(dimension): if filters.get(dimension.fieldname):
additional_conditions += """ and {0} in (%({0})s) """.format(dimension) if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname))
additional_conditions += "and {0} in %({0})s".format(dimension.fieldname)
query_filters.update({ query_filters.update({
dimension: filters.get(dimension) dimension.fieldname: filters.get(dimension.fieldname)
}) })
gle = frappe.db.sql(""" gle = frappe.db.sql("""

View File

@ -139,12 +139,14 @@
"read_only": 1 "read_only": 1
} }
], ],
"is_tree": 1,
"links": [], "links": [],
"modified": "2020-03-02 19:34:28.362267", "modified": "2020-03-18 18:00:08.885805",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Location", "name": "Location",
"name_case": "Title Case", "name_case": "Title Case",
"nsm_parent_field": "parent_location",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@ -499,7 +499,8 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
reference_doctype: me.frm.doctype, reference_doctype: me.frm.doctype,
reference_name: me.frm.docname, reference_name: me.frm.docname,
content: __('Reason for hold: ')+data.reason_for_hold, content: __('Reason for hold: ')+data.reason_for_hold,
comment_email: frappe.session.user comment_email: frappe.session.user,
comment_by: frappe.session.user_fullname
}, },
callback: function(r) { callback: function(r) {
if(!r.exc) { if(!r.exc) {

View File

@ -4,15 +4,17 @@
// attach required files // attach required files
{% include 'erpnext/public/js/controllers/buying.js' %}; {% include 'erpnext/public/js/controllers/buying.js' %};
frappe.ui.form.on('Suppier Quotation', {
setup: function(frm) {
frm.custom_make_buttons = {
'Purchase Order': 'Purchase Order'
}
}
});
erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({
setup: function() {
this.frm.custom_make_buttons = {
'Purchase Order': 'Purchase Order',
'Quotation': 'Quotation',
'Subscription': 'Subscription'
}
this._super();
},
refresh: function() { refresh: function() {
var me = this; var me = this;
this._super(); this._super();

View File

@ -1,22 +0,0 @@
{
"cards": [],
"charts": [],
"creation": "2020-01-28 11:49:55.003637",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Desk Page",
"extends_another_page": 0,
"icon": "",
"idx": 0,
"is_standard": 1,
"label": "Communication",
"modified": "2020-03-12 16:30:40.534226",
"modified_by": "Administrator",
"module": "Communication",
"name": "Communication",
"owner": "Administrator",
"pin_to_bottom": 0,
"pin_to_top": 0,
"shortcuts": []
}

View File

@ -19,6 +19,7 @@ from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_t
from erpnext.exceptions import InvalidCurrency from erpnext.exceptions import InvalidCurrency
from six import text_type from six import text_type
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
from erpnext.stock.get_item_details import get_item_warehouse
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
@ -1126,16 +1127,16 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname,
""" """
Returns a Sales Order Item child item containing the default values Returns a Sales Order Item child item containing the default values
""" """
p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name) p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
child_item = frappe.new_doc('Sales Order Item', p_doctype, child_docname) child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname)
item = frappe.get_doc("Item", item_code) item = frappe.get_doc("Item", item_code)
child_item.item_code = item.item_code child_item.item_code = item.item_code
child_item.item_name = item.item_name child_item.item_name = item.item_name
child_item.description = item.description child_item.description = item.description
child_item.reqd_by_date = p_doctype.delivery_date child_item.reqd_by_date = p_doc.delivery_date
child_item.uom = item.stock_uom child_item.uom = item.stock_uom
child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.warehouse = p_doctype.set_warehouse or p_doctype.items[0].warehouse child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
return child_item return child_item
@ -1143,13 +1144,13 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
""" """
Returns a Purchase Order Item child item containing the default values Returns a Purchase Order Item child item containing the default values
""" """
p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name) p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
child_item = frappe.new_doc('Purchase Order Item', p_doctype, child_docname) child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname)
item = frappe.get_doc("Item", item_code) item = frappe.get_doc("Item", item_code)
child_item.item_code = item.item_code child_item.item_code = item.item_code
child_item.item_name = item.item_name child_item.item_name = item.item_name
child_item.description = item.description child_item.description = item.description
child_item.schedule_date = p_doctype.schedule_date child_item.schedule_date = p_doc.schedule_date
child_item.uom = item.stock_uom child_item.uom = item.stock_uom
child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_rate = 1 # Initiallize value will update in parent validation

View File

@ -672,19 +672,32 @@ class BuyingController(StockController):
# If asset has to be auto created # If asset has to be auto created
# Check for asset naming series # Check for asset naming series
if item_data.get('asset_naming_series'): if item_data.get('asset_naming_series'):
created_assets = []
for qty in range(cint(d.qty)): for qty in range(cint(d.qty)):
self.make_asset(d) asset = self.make_asset(d)
is_plural = 's' if cint(d.qty) != 1 else '' created_assets.append(asset)
messages.append(_('{0} Asset{2} Created for <b>{1}</b>').format(cint(d.qty), d.item_code, is_plural))
if len(created_assets) > 5:
# dont show asset form links if more than 5 assets are created
messages.append(_('{} Asset{} created for {}').format(len(created_assets), is_plural, frappe.bold(d.item_code)))
else: else:
frappe.throw(_("Row {1}: Asset Naming Series is mandatory for the auto creation for item {0}") assets_link = list(map(lambda d: frappe.utils.get_link_to_form('Asset', d), created_assets))
.format(d.item_code, d.idx)) assets_link = frappe.bold(','.join(assets_link))
is_plural = 's' if len(created_assets) != 1 else ''
messages.append(
_('Asset{} {assets_link} created for {}').format(is_plural, frappe.bold(d.item_code), assets_link=assets_link)
)
else: else:
messages.append(_("Assets not created for <b>{0}</b>. You will have to create asset manually.") frappe.throw(_("Row {}: Asset Naming Series is mandatory for the auto creation for item {}")
.format(d.item_code)) .format(d.idx, frappe.bold(d.item_code)))
else:
messages.append(_("Assets not created for {0}. You will have to create asset manually.")
.format(frappe.bold(d.item_code)))
for message in messages: for message in messages:
frappe.msgprint(message, title="Success") frappe.msgprint(message, title="Success", indicator="green")
def make_asset(self, row): def make_asset(self, row):
if not row.asset_location: if not row.asset_location:
@ -716,6 +729,8 @@ class BuyingController(StockController):
asset.set_missing_values() asset.set_missing_values()
asset.insert() asset.insert()
return asset.name
def update_fixed_asset(self, field, delete_asset = False): def update_fixed_asset(self, field, delete_asset = False):
for d in self.get("items"): for d in self.get("items"):
if d.is_fixed_asset: if d.is_fixed_asset:
@ -745,7 +760,7 @@ class BuyingController(StockController):
asset.supplier = None asset.supplier = None
if asset.docstatus == 1 and delete_asset: if asset.docstatus == 1 and delete_asset:
frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}.\ frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}.\
Please cancel the it to continue.').format(asset.name)) Please cancel the it to continue.').format(frappe.utils.get_link_to_form('Asset', asset.name)))
asset.flags.ignore_validate_update_after_submit = True asset.flags.ignore_validate_update_after_submit = True
asset.flags.ignore_mandatory = True asset.flags.ignore_mandatory = True

View File

@ -3,6 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
import erpnext
from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.desk.reportview import get_match_cond, get_filters_cond
from frappe.utils import nowdate, getdate from frappe.utils import nowdate, getdate
from collections import defaultdict from collections import defaultdict
@ -129,23 +130,26 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
}) })
def tax_account_query(doctype, txt, searchfield, start, page_len, filters): def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
company_currency = erpnext.get_company_currency(filters.get('company'))
tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount
where tabAccount.docstatus!=2 where tabAccount.docstatus!=2
and account_type in (%s) and account_type in (%s)
and is_group = 0 and is_group = 0
and company = %s and company = %s
and account_currency = %s
and `%s` LIKE %s and `%s` LIKE %s
order by idx desc, name order by idx desc, name
limit %s, %s""" % limit %s, %s""" %
(", ".join(['%s']*len(filters.get("account_type"))), "%s", searchfield, "%s", "%s", "%s"), (", ".join(['%s']*len(filters.get("account_type"))), "%s", "%s", searchfield, "%s", "%s", "%s"),
tuple(filters.get("account_type") + [filters.get("company"), "%%%s%%" % txt, tuple(filters.get("account_type") + [filters.get("company"), company_currency, "%%%s%%" % txt,
start, page_len])) start, page_len]))
if not tax_accounts: if not tax_accounts:
tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount
where tabAccount.docstatus!=2 and is_group = 0 where tabAccount.docstatus!=2 and is_group = 0
and company = %s and `%s` LIKE %s limit %s, %s""" and company = %s and account_currency = %s and `%s` LIKE %s limit %s, %s""" #nosec
% ("%s", searchfield, "%s", "%s", "%s"), % ("%s", "%s", searchfield, "%s", "%s", "%s"),
(filters.get("company"), "%%%s%%" % txt, start, page_len)) (filters.get("company"), company_currency, "%%%s%%" % txt, start, page_len))
return tax_accounts return tax_accounts
@ -175,6 +179,12 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
# scan description only if items are less than 50000 # scan description only if items are less than 50000
description_cond = 'or tabItem.description LIKE %(txt)s' description_cond = 'or tabItem.description LIKE %(txt)s'
extra_cond = " and tabItem.has_variants=0"
if (filters and isinstance(filters, dict)
and filters.get("doctype") == "BOM"):
extra_cond = ""
del filters["doctype"]
return frappe.db.sql("""select tabItem.name, return frappe.db.sql("""select tabItem.name,
if(length(tabItem.item_name) > 40, if(length(tabItem.item_name) > 40,
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name, concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
@ -184,11 +194,11 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
{columns} {columns}
from tabItem from tabItem
where tabItem.docstatus < 2 where tabItem.docstatus < 2
and tabItem.has_variants=0
and tabItem.disabled=0 and tabItem.disabled=0
and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00') and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00')
and ({scond} or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s) and ({scond} or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
{description_cond}) {description_cond})
{extra_cond}
{fcond} {mcond} {fcond} {mcond}
order by order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
@ -199,6 +209,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
key=searchfield, key=searchfield,
columns=columns, columns=columns,
scond=searchfields, scond=searchfields,
extra_cond=extra_cond,
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
mcond=get_match_cond(doctype).replace('%', '%%'), mcond=get_match_cond(doctype).replace('%', '%%'),
description_cond = description_cond), description_cond = description_cond),

View File

@ -69,6 +69,17 @@ status_map = {
["Cancelled", "eval:self.docstatus==2"], ["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"], ["Closed", "eval:self.status=='Closed'"],
], ],
"Purchase Invoice": [
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
["Return", "eval:self.is_return==1 and self.docstatus==1"],
["Debit Note Issued",
"eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"],
],
"Material Request": [ "Material Request": [
["Draft", None], ["Draft", None],
["Stopped", "eval:self.status == 'Stopped'"], ["Stopped", "eval:self.status == 'Stopped'"],

View File

@ -238,7 +238,7 @@ class StockController(AccountsController):
for d in self.items: for d in self.items:
if not d.batch_no: continue if not d.batch_no: continue
serial_nos = [d.name for d in frappe.get_all("Serial No", {'batch_no': d.batch_no})] serial_nos = [sr.name for sr in frappe.get_all("Serial No", {'batch_no': d.batch_no})]
if serial_nos: if serial_nos:
frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None) frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None)

View File

@ -172,7 +172,6 @@
"options": "Customer" "options": "Customer"
}, },
{ {
"depends_on": "eval: doc.source==\"Campaign\"",
"fieldname": "campaign_name", "fieldname": "campaign_name",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Campaign Name", "label": "Campaign Name",

View File

@ -132,10 +132,17 @@ class Lead(SellingController):
# do not create an address if no fields are available, # do not create an address if no fields are available,
# skipping country since the system auto-sets it from system defaults # skipping country since the system auto-sets it from system defaults
if not any([self.get(field) for field in address_fields if field != "country"]): address = frappe.new_doc("Address")
mandatory_fields = [ df.fieldname for df in address.meta.fields if df.reqd ]
if not all([self.get(field) for field in mandatory_fields]):
frappe.msgprint(_('Missing mandatory fields in address. \
{0} to create address' ).format("<a href='desk#Form/Address/New Address 1' \
> Click here </a>"),
alert=True, indicator='yellow')
return return
address = frappe.new_doc("Address")
address.update({addr_field: self.get(addr_field) for addr_field in address_fields}) address.update({addr_field: self.get(addr_field) for addr_field in address_fields})
address.update({info_field: self.get(info_field) for info_field in info_fields}) address.update({info_field: self.get(info_field) for info_field in info_fields})
address.insert() address.insert()

View File

@ -216,7 +216,8 @@
{ {
"fieldname": "opportunity_amount", "fieldname": "opportunity_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Opportunity Amount" "label": "Opportunity Amount",
"options": "currency"
}, },
{ {
"default": "0", "default": "0",
@ -422,7 +423,7 @@
"icon": "fa fa-info-sign", "icon": "fa fa-info-sign",
"idx": 195, "idx": 195,
"links": [], "links": [],
"modified": "2020-01-13 16:18:44.477818", "modified": "2020-03-20 12:28:45.228994",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Opportunity", "name": "Opportunity",

View File

@ -6,18 +6,13 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils.make_random import get_random from frappe.utils.make_random import get_random
from erpnext.assets.doctype.asset.asset import make_purchase_invoice, make_sales_invoice from erpnext.assets.doctype.asset.asset import make_sales_invoice
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset
def work(): def work():
frappe.set_user(frappe.db.get_global('demo_accounts_user')) frappe.set_user(frappe.db.get_global('demo_accounts_user'))
asset_list = make_asset_purchase_entry()
if not asset_list:
# fixed_asset.work() already run
return
# Enable booking asset depreciation entry automatically # Enable booking asset depreciation entry automatically
frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
@ -33,19 +28,6 @@ def work():
# Sell a random asset # Sell a random asset
sell_an_asset() sell_an_asset()
def make_asset_purchase_entry():
asset_list = frappe.get_all("Asset", filters={"purchase_invoice": ["in", ("", None)]},
fields=["name", "item_code", "gross_purchase_amount", "company", "purchase_date"])
# make purchase invoice
for asset in asset_list:
pi = make_purchase_invoice(asset.name, asset.item_code, asset.gross_purchase_amount,
asset.company, asset.purchase_date)
pi.supplier = get_random("Supplier")
pi.save()
pi.submit()
return asset_list
def sell_an_asset(): def sell_an_asset():
asset = get_random_asset() asset = get_random_asset()
@ -56,6 +38,7 @@ def sell_an_asset():
si.save() si.save()
si.submit() si.submit()
def get_random_asset(): def get_random_asset():
return frappe.db.sql(""" select name, item_code, value_after_depreciation, gross_purchase_amount return frappe.db.sql(""" select name, item_code, value_after_depreciation, gross_purchase_amount
from `tabAsset` from `tabAsset`

View File

@ -1,274 +1,90 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "field:assessment_group_name", "autoname": "field:assessment_group_name",
"beta": 0,
"creation": "2016-08-04 04:42:48.319388", "creation": "2016-08-04 04:42:48.319388",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "",
"editable_grid": 1, "editable_grid": 1,
"field_order": [
"assessment_group_name",
"is_group",
"section_break_2",
"parent_assessment_group",
"lft",
"rgt",
"old_parent"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "assessment_group_name", "fieldname": "assessment_group_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Assessment Group Name", "label": "Assessment Group Name",
"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": 1, "reqd": 1,
"search_index": 0, "unique": 1
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "is_group", "fieldname": "is_group",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "label": "Is Group"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Group",
"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,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_2", "fieldname": "section_break_2",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 1, "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,
"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,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "parent_assessment_group", "fieldname": "parent_assessment_group",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Parent Assessment Group", "label": "Parent Assessment Group",
"length": 0,
"no_copy": 0,
"options": "Assessment Group", "options": "Assessment Group",
"permlevel": 0, "reqd": 1
"precision": "",
"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,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "lft", "fieldname": "lft",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 0, "label": "lft"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "lft",
"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,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rgt", "fieldname": "rgt",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 0, "label": "rgt"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "rgt",
"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,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "old_parent", "fieldname": "old_parent",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "old_parent", "label": "old_parent",
"length": 0, "options": "Assessment Group"
"no_copy": 0,
"options": "Assessment Group",
"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,
"unique": 0
} }
], ],
"has_web_view": 0, "is_tree": 1,
"hide_heading": 0, "links": [],
"hide_toolbar": 0, "modified": "2020-03-18 18:01:14.710416",
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-11-10 19:09:25.366400",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Education", "module": "Education",
"name": "Assessment Group", "name": "Assessment Group",
"name_case": "", "nsm_parent_field": "parent_assessment_group",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Academics User", "role": "Academics User",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 1, "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Education", "restrict_to_domain": "Education",
"show_name_in_global_search": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC"
"track_changes": 0,
"track_seen": 0
} }

View File

@ -74,7 +74,7 @@
} }
], ],
"image_field": "hero_image", "image_field": "hero_image",
"modified": "2019-06-12 12:34:23.748157", "modified": "2020-03-29 12:50:27.677589",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Education", "module": "Education",
"name": "Course", "name": "Course",
@ -103,6 +103,30 @@
"role": "Instructor", "role": "Instructor",
"share": 1, "share": 1,
"write": 1 "write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Administrator",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Education Manager",
"share": 1,
"write": 1
} }
], ],
"restrict_to_domain": "Education", "restrict_to_domain": "Education",

View File

@ -0,0 +1,60 @@
{
"custom_fields": [
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"creation": "2019-12-02 11:00:03.432994",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Contact",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "is_billing_contact",
"fieldtype": "Check",
"hidden": 0,
"idx": 27,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "is_primary_contact",
"label": "Is Billing Contact",
"length": 0,
"modified": "2019-12-02 11:00:03.432994",
"modified_by": "Administrator",
"name": "Contact-is_billing_contact",
"no_copy": 0,
"options": null,
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
}
],
"custom_perms": [],
"doctype": "Contact",
"property_setters": [],
"sync_on_migrate": 1
}

View File

@ -1,542 +1,205 @@
{ {
"allow_copy": 0, "actions": [],
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "field:healthcare_service_unit_name", "autoname": "field:healthcare_service_unit_name",
"beta": 1, "beta": 1,
"creation": "2016-09-21 13:48:14.731437", "creation": "2016-09-21 13:48:14.731437",
"custom": 0,
"description": "Healthcare Service Unit", "description": "Healthcare Service Unit",
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Setup", "document_type": "Setup",
"editable_grid": 1, "editable_grid": 1,
"field_order": [
"healthcare_service_unit_name",
"parent_healthcare_service_unit",
"is_group",
"service_unit_type",
"allow_appointments",
"overlap_appointments",
"inpatient_occupancy",
"occupancy_status",
"warehouse",
"company",
"lft",
"rgt",
"old_parent"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "healthcare_service_unit_name", "fieldname": "healthcare_service_unit_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1, "in_global_search": 1,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Service Unit", "label": "Service Unit",
"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": 1, "reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1 "unique": 1
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1, "bold": 1,
"collapsible": 0,
"columns": 0,
"fieldname": "parent_healthcare_service_unit", "fieldname": "parent_healthcare_service_unit",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Parent Service Unit", "label": "Parent Service Unit",
"length": 0, "options": "Healthcare Service Unit"
"no_copy": 0,
"options": "Healthcare Service Unit",
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1, "bold": 1,
"collapsible": 0,
"columns": 0,
"default": "0", "default": "0",
"depends_on": "eval:doc.inpatient_occupancy != 1 && doc.allow_appointments != 1", "depends_on": "eval:doc.inpatient_occupancy != 1 && doc.allow_appointments != 1",
"fieldname": "is_group", "fieldname": "is_group",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0, "label": "Is Group"
"label": "Is Group",
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1, "bold": 1,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_group != 1", "depends_on": "eval:doc.is_group != 1",
"fieldname": "service_unit_type", "fieldname": "service_unit_type",
"fieldtype": "Link", "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": "Service Unit Type", "label": "Service Unit Type",
"length": 0, "options": "Healthcare Service Unit Type"
"no_copy": 0,
"options": "Healthcare Service Unit Type",
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1, "bold": 1,
"collapsible": 0,
"columns": 0,
"default": "0", "default": "0",
"depends_on": "eval:doc.is_group != 1 && doc.inpatient_occupancy != 1", "depends_on": "eval:doc.is_group != 1 && doc.inpatient_occupancy != 1",
"fetch_from": "service_unit_type.allow_appointments", "fetch_from": "service_unit_type.allow_appointments",
"fieldname": "allow_appointments", "fieldname": "allow_appointments",
"fieldtype": "Check", "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": "Allow Appointments", "label": "Allow Appointments",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0, "read_only": 1
"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_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0", "default": "0",
"depends_on": "eval:doc.is_group != 1 && doc.allow_appointments == 1 && doc.inpatient_occupany != 1", "depends_on": "eval:doc.is_group != 1 && doc.allow_appointments == 1 && doc.inpatient_occupany != 1",
"fetch_from": "service_unit_type.overlap_appointments", "fetch_from": "service_unit_type.overlap_appointments",
"fieldname": "overlap_appointments", "fieldname": "overlap_appointments",
"fieldtype": "Check", "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": "Allow Overlap", "label": "Allow Overlap",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0, "read_only": 1
"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_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1, "bold": 1,
"collapsible": 0,
"columns": 0,
"default": "0", "default": "0",
"depends_on": "eval:doc.allow_appointments != 1 && doc.is_group != 1", "depends_on": "eval:doc.allow_appointments != 1 && doc.is_group != 1",
"fetch_from": "service_unit_type.inpatient_occupancy", "fetch_from": "service_unit_type.inpatient_occupancy",
"fieldname": "inpatient_occupancy", "fieldname": "inpatient_occupancy",
"fieldtype": "Check", "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": "Inpatient Occupancy", "label": "Inpatient Occupancy",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0, "search_index": 1
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"depends_on": "eval:doc.inpatient_occupancy == 1", "depends_on": "eval:doc.inpatient_occupancy == 1",
"fieldname": "occupancy_status", "fieldname": "occupancy_status",
"fieldtype": "Select", "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": "Occupancy Status", "label": "Occupancy Status",
"length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Vacant\nOccupied", "options": "Vacant\nOccupied",
"permlevel": 0, "read_only": 1
"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_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1, "bold": 1,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_group != 1", "depends_on": "eval:doc.is_group != 1",
"fieldname": "warehouse", "fieldname": "warehouse",
"fieldtype": "Link", "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": "Warehouse", "label": "Warehouse",
"length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Warehouse", "options": "Warehouse"
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Company", "label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company", "options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 1, "remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "lft", "fieldname": "lft",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 1, "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": "lft", "label": "lft",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "search_index": 1
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rgt", "fieldname": "rgt",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 1, "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": "rgt", "label": "rgt",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "search_index": 1
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "old_parent", "fieldname": "old_parent",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Old Parent", "label": "Old Parent",
"length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Healthcare Service Unit", "options": "Healthcare Service Unit",
"permlevel": 0,
"precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "report_hide": 1
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "is_tree": 1,
"hide_heading": 0, "links": [],
"hide_toolbar": 0, "modified": "2020-03-18 18:02:23.713439",
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-04 21:09:52.261882",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Healthcare Service Unit", "name": "Healthcare Service Unit",
"name_case": "", "nsm_parent_field": "parent_healthcare_service_unit",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Nursing User", "role": "Nursing User",
"set_user_permissions": 0, "share": 1
"share": 1,
"submit": 0,
"write": 0
}, },
{ {
"amend": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Healthcare Administrator", "role": "Healthcare Administrator",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 0,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Physician", "role": "Physician",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 1, "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare", "restrict_to_domain": "Healthcare",
"search_fields": "healthcare_service_unit_name", "search_fields": "healthcare_service_unit_name",
"show_name_in_global_search": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"title_field": "healthcare_service_unit_name", "title_field": "healthcare_service_unit_name",
"track_changes": 1, "track_changes": 1
"track_seen": 0,
"track_views": 0
} }

View File

@ -386,5 +386,5 @@ def get_procedure_prescribed(patient):
return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner, return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner,
ct.encounter_date, pp.practitioner, pp.date, pp.department ct.encounter_date, pp.practitioner, pp.date, pp.department
from `tabPatient Encounter` ct, `tabProcedure Prescription` pp from `tabPatient Encounter` ct, `tabProcedure Prescription` pp
where ct.patient='{0}' and pp.parent=ct.name and pp.appointment_booked=0 where ct.patient=%(patient)s and pp.parent=ct.name and pp.appointment_booked=0
order by ct.creation desc""".format(patient)) order by ct.creation desc""", {"patient": patient})

View File

@ -129,11 +129,13 @@
], ],
"icon": "fa fa-sitemap", "icon": "fa fa-sitemap",
"idx": 1, "idx": 1,
"is_tree": 1,
"links": [], "links": [],
"modified": "2019-12-12 14:48:35.254308", "modified": "2020-03-18 18:03:27.784362",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Department", "name": "Department",
"nsm_parent_field": "parent_department",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@ -48,12 +48,17 @@ def get_abbreviated_name(name, company):
@frappe.whitelist() @frappe.whitelist()
def get_children(doctype, parent=None, company=None, is_root=False): def get_children(doctype, parent=None, company=None, is_root=False):
condition = '' condition = ''
var_dict = {
"name": get_root_of("Department"),
"parent": parent,
"company": company,
}
if company == parent: if company == parent:
condition = "name='{0}'".format(get_root_of("Department")) condition = "name=%(name)s"
elif company: elif company:
condition = "parent_department='{0}' and company='{1}'".format(parent, company) condition = "parent_department=%(parent)s and company=%(company)s"
else: else:
condition = "parent_department = '{0}'".format(parent) condition = "parent_department = %(parent)s"
return frappe.db.sql(""" return frappe.db.sql("""
select select
@ -62,7 +67,7 @@ def get_children(doctype, parent=None, company=None, is_root=False):
from `tab{doctype}` from `tab{doctype}`
where where
{condition} {condition}
order by name""".format(doctype=doctype, condition=condition), as_dict=1) order by name""".format(doctype=doctype, condition=condition), var_dict, as_dict=1)
@frappe.whitelist() @frappe.whitelist()
def add_node(): def add_node():

View File

@ -190,6 +190,7 @@ def get_benefit_component_amount(employee, start_date, end_date, salary_componen
component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component", component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component",
salary_component, ["max_benefit_amount", "depends_on_payment_days"]) salary_component, ["max_benefit_amount", "depends_on_payment_days"])
benefit_amount = 0
if benefit_application: if benefit_application:
benefit_amount = frappe.db.get_value("Employee Benefit Application Detail", benefit_amount = frappe.db.get_value("Employee Benefit Application Detail",
{"parent": benefit_application[0][0], "earning_component": salary_component}, "amount") {"parent": benefit_application[0][0], "earning_component": salary_component}, "amount")

View File

@ -71,7 +71,7 @@
"label": "Amount", "label": "Amount",
"oldfieldname": "tax_amount", "oldfieldname": "tax_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "currency" "options": "Company:company:default_currency"
}, },
{ {
"columns": 2, "columns": 2,
@ -81,7 +81,7 @@
"label": "Total", "label": "Total",
"oldfieldname": "total", "oldfieldname": "total",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "currency", "options": "Company:company:default_currency",
"read_only": 1 "read_only": 1
}, },
{ {
@ -95,7 +95,7 @@
], ],
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2019-12-11 13:50:02.883328", "modified": "2020-03-11 13:25:06.721917",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Expense Taxes and Charges", "name": "Expense Taxes and Charges",

View File

@ -130,7 +130,7 @@ class LeaveApplication(Document):
if self.status == "Approved": if self.status == "Approved":
for dt in daterange(getdate(self.from_date), getdate(self.to_date)): for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
date = dt.strftime("%Y-%m-%d") date = dt.strftime("%Y-%m-%d")
status = "Half Day" if date == self.half_day_date else "On Leave" status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave"
attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee,
attendance_date = date, docstatus = ('!=', 2))) attendance_date = date, docstatus = ('!=', 2)))

View File

@ -6,11 +6,14 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
from frappe.utils import nowdate,flt, cstr,random_string from frappe.utils import nowdate,flt, cstr,random_string
# test_records = frappe.get_test_records('Vehicle Log')
class TestVehicleLog(unittest.TestCase): class TestVehicleLog(unittest.TestCase):
def test_make_vehicle_log_and_syncing_of_odometer_value(self): def test_make_vehicle_log_and_syncing_of_odometer_value(self):
employee_id=frappe.db.sql("""select name from `tabEmployee` order by modified desc limit 1""")[0][0] employee_id = frappe.db.sql("""select name from `tabEmployee` where status='Active' order by modified desc limit 1""")
employee_id = employee_id[0][0] if employee_id else None
license_plate = get_vehicle(employee_id) license_plate = get_vehicle(employee_id)
vehicle_log = frappe.get_doc({ vehicle_log = frappe.get_doc({
"doctype": "Vehicle Log", "doctype": "Vehicle Log",
"license_plate": cstr(license_plate), "license_plate": cstr(license_plate),

View File

@ -3,11 +3,6 @@
frappe.ui.form.on("Vehicle Log", { frappe.ui.form.on("Vehicle Log", {
refresh: function(frm) { refresh: function(frm) {
if(frm.doc.license_plate && frm.doc.__islocal){
frm.events.set_vehicle_details(frm);
}
if(frm.doc.docstatus == 1) { if(frm.doc.docstatus == 1) {
frm.add_custom_button(__('Expense Claim'), function() { frm.add_custom_button(__('Expense Claim'), function() {
frm.events.expense_claim(frm); frm.events.expense_claim(frm);
@ -16,27 +11,6 @@ frappe.ui.form.on("Vehicle Log", {
} }
}, },
license_plate: function(frm) {
if(frm.doc.license_plate){
frm.events.set_vehicle_details(frm);
}
},
set_vehicle_details: function(frm) {
frappe.call({
method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model",
args: {
license_plate: frm.doc.license_plate
},
callback: function(r) {
frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[0]);
frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[1]);
frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "last_odometer", r.message[2]);
frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "employee", r.message[3]);
}
});
},
expense_claim: function(frm){ expense_claim: function(frm){
frappe.call({ frappe.call({
method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim", method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim",

View File

@ -1,5 +1,4 @@
{ {
"actions": [],
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2016-09-03 14:14:51.788550", "creation": "2016-09-03 14:14:51.788550",
"doctype": "DocType", "doctype": "DocType",
@ -56,6 +55,8 @@
"reqd": 1 "reqd": 1
}, },
{ {
"fetch_from": "license_plate.employee",
"fetch_if_empty": 1,
"fieldname": "employee", "fieldname": "employee",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
@ -73,11 +74,13 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"fetch_from": "license_plate.model",
"fieldname": "model", "fieldname": "model",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"label": "Model" "label": "Model"
}, },
{ {
"fetch_from": "license_plate.make",
"fieldname": "make", "fieldname": "make",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"label": "Make" "label": "Make"
@ -152,6 +155,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"fetch_from": "license_plate.last_odometer",
"fieldname": "last_odometer", "fieldname": "last_odometer",
"fieldtype": "Int", "fieldtype": "Int",
"label": "last Odometer Value ", "label": "last Odometer Value ",
@ -164,8 +168,7 @@
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "modified": "2020-03-18 16:45:45.060761",
"modified": "2020-01-28 12:43:34.419647",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Vehicle Log", "name": "Vehicle Log",

View File

@ -12,18 +12,7 @@ from frappe.model.document import Document
class VehicleLog(Document): class VehicleLog(Document):
def validate(self): def validate(self):
if flt(self.odometer) < flt(self.last_odometer): if flt(self.odometer) < flt(self.last_odometer):
frappe.throw(_("Current Odometer reading entered should be greater than initial Vehicle Odometer {0}").format(self.last_odometer)) frappe.throw(_("Current Odometer Value should be greater than Last Odometer Value {0}").format(self.last_odometer))
for service_detail in self.service_detail:
if (service_detail.service_item or service_detail.type or service_detail.frequency or service_detail.expense_amount):
if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount):
frappe.throw(_("Service Item,Type,frequency and expense amount are required"))
def before_insert(self):
model_details = get_make_model(self.license_plate)
self.make = model_details[0]
self.model = model_details[1]
self.last_odometer = model_details[2]
self.employee = model_details[3]
def on_submit(self): def on_submit(self):
frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer) frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer)
@ -34,35 +23,26 @@ class VehicleLog(Document):
updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled
frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value) frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value)
@frappe.whitelist()
def get_make_model(license_plate):
vehicle=frappe.get_doc("Vehicle",license_plate)
return (vehicle.make, vehicle.model, vehicle.last_odometer, vehicle.employee)
@frappe.whitelist() @frappe.whitelist()
def make_expense_claim(docname): def make_expense_claim(docname):
def check_exp_claim_exists(): expense_claim = frappe.db.exists("Expense Claim", {"vehicle_log": docname})
exp_claim = frappe.db.sql("""select name from `tabExpense Claim` where vehicle_log=%s""",vehicle_log.name) if expense_claim:
return exp_claim[0][0] if exp_claim else "" frappe.throw(_("Expense Claim {0} already exists for the Vehicle Log").format(expense_claim))
def calc_service_exp():
total_exp_amt=0
exp_claim = check_exp_claim_exists()
if exp_claim:
frappe.throw(_("Expense Claim {0} already exists for the Vehicle Log").format(exp_claim))
for serdetail in vehicle_log.service_detail:
total_exp_amt = total_exp_amt + serdetail.expense_amount
return total_exp_amt
vehicle_log = frappe.get_doc("Vehicle Log", docname) vehicle_log = frappe.get_doc("Vehicle Log", docname)
service_expense = sum([flt(d.expense_amount) for d in vehicle_log.service_detail])
claim_amount = service_expense + flt(vehicle_log.price)
if not claim_amount:
frappe.throw(_("No additional expenses has been added"))
exp_claim = frappe.new_doc("Expense Claim") exp_claim = frappe.new_doc("Expense Claim")
exp_claim.employee = vehicle_log.employee exp_claim.employee = vehicle_log.employee
exp_claim.vehicle_log = vehicle_log.name exp_claim.vehicle_log = vehicle_log.name
exp_claim.remark = _("Expense Claim for Vehicle Log {0}").format(vehicle_log.name) exp_claim.remark = _("Expense Claim for Vehicle Log {0}").format(vehicle_log.name)
fuel_price=vehicle_log.price
total_claim_amt=calc_service_exp() + fuel_price
exp_claim.append("expenses", { exp_claim.append("expenses", {
"expense_date": vehicle_log.date, "expense_date": vehicle_log.date,
"description": _("Vehicle Expenses"), "description": _("Vehicle Expenses"),
"amount":total_claim_amt "amount": claim_amount
}) })
return exp_claim.as_dict() return exp_claim.as_dict()

View File

@ -1,153 +1,57 @@
{ {
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2016-09-03 19:20:14.561962", "creation": "2016-09-03 19:20:14.561962",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Document", "document_type": "Document",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"service_item",
"type",
"frequency",
"expense_amount"
],
"fields": [ "fields": [
{ {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "service_item", "fieldname": "service_item",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Service Item", "label": "Service Item",
"length": 0,
"no_copy": 0,
"options": "\nBrake Oil\nBrake Pad\nClutch Plate\nEngine Oil\nOil Change\nWheels", "options": "\nBrake Oil\nBrake Pad\nClutch Plate\nEngine Oil\nOil Change\nWheels",
"permlevel": 0, "reqd": 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,
"unique": 0
}, },
{ {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "type", "fieldname": "type",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Type", "label": "Type",
"length": 0,
"no_copy": 0,
"options": "\nInspection\nService\nChange", "options": "\nInspection\nService\nChange",
"permlevel": 0, "reqd": 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,
"unique": 0
}, },
{ {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "frequency", "fieldname": "frequency",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Frequency", "label": "Frequency",
"length": 0,
"no_copy": 0,
"options": "\nMileage\nMonthly\nQuarterly\nHalf Yearly\nYearly", "options": "\nMileage\nMonthly\nQuarterly\nHalf Yearly\nYearly",
"permlevel": 0, "reqd": 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,
"unique": 0
}, },
{ {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "expense_amount", "fieldname": "expense_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Expense", "label": "Expense",
"length": 0, "reqd": 1
"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,
"unique": 0
} }
], ],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "modified": "2020-03-18 16:49:46.645004",
"modified": "2017-01-09 11:10:29.476907",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Vehicle Service", "name": "Vehicle Service",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"quick_entry": 1, "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 1, "track_changes": 1
"track_seen": 0
} }

View File

@ -273,11 +273,11 @@ class TestLoan(unittest.TestCase):
penalty_amount = (accrued_interest_amount * 4 * 25) / (100 * days_in_year(get_datetime(first_date).year)) penalty_amount = (accrued_interest_amount * 4 * 25) / (100 * days_in_year(get_datetime(first_date).year))
lia = frappe.get_all("Loan Interest Accrual", fields=["is_paid"], lia1 = frappe.get_value("Loan Interest Accrual", {"loan": loan.name, "is_paid": 1}, 'name')
filters={"loan": loan.name}, order_by="posting_date") lia2 = frappe.get_value("Loan Interest Accrual", {"loan": loan.name, "is_paid": 0}, 'name')
self.assertTrue(lia[0].get('is_paid')) self.assertTrue(lia1)
self.assertFalse(lia[1].get('is_paid')) self.assertTrue(lia2)
def test_security_shortfall(self): def test_security_shortfall(self):
pledges = [] pledges = []
@ -294,18 +294,21 @@ class TestLoan(unittest.TestCase):
make_loan_disbursement_entry(loan.name, loan.loan_amount) make_loan_disbursement_entry(loan.name, loan.loan_amount)
frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = %s frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 100
where loan_security=%s""", (100, 'Test Security 2')) where loan_security='Test Security 2'""")
check_for_ltv_shortfall() check_for_ltv_shortfall()
loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name}) loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
self.assertTrue(loan_security_shortfall) self.assertTrue(loan_security_shortfall)
self.assertEquals(loan_security_shortfall.loan_amount, 1000000.00) self.assertEquals(loan_security_shortfall.loan_amount, 1000000.00)
self.assertEquals(loan_security_shortfall.security_value, 400000.00) self.assertEquals(loan_security_shortfall.security_value, 400000.00)
self.assertEquals(loan_security_shortfall.shortfall_amount, 600000.00) self.assertEquals(loan_security_shortfall.shortfall_amount, 600000.00)
frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
where loan_security='Test Security 2'""")
def create_loan_accounts(): def create_loan_accounts():
if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"): if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
frappe.get_doc({ frappe.get_doc({
@ -399,7 +402,8 @@ def create_loan_security_type():
"doctype": "Loan Security Type", "doctype": "Loan Security Type",
"loan_security_type": "Stock", "loan_security_type": "Stock",
"unit_of_measure": "Nos", "unit_of_measure": "Nos",
"haircut": 50.00 "haircut": 50.00,
"loan_to_value_ratio": 50
}).insert(ignore_permissions=True) }).insert(ignore_permissions=True)
def create_loan_security(): def create_loan_security():

View File

@ -130,6 +130,7 @@ def make_accrual_interest_entry_for_term_loans(posting_date=None):
loan.loan_account, loan.principal_amount + loan.balance_loan_amount, loan.interest_amount, loan.loan_account, loan.principal_amount + loan.balance_loan_amount, loan.interest_amount,
payable_principal = loan.principal_amount , posting_date=posting_date) payable_principal = loan.principal_amount , posting_date=posting_date)
if accrued_entries:
frappe.db.sql("""UPDATE `tabRepayment Schedule` frappe.db.sql("""UPDATE `tabRepayment Schedule`
SET is_accrued = 1 where name in (%s)""" #nosec SET is_accrued = 1 where name in (%s)""" #nosec
% ", ".join(['%s']*len(accrued_entries)), tuple(accrued_entries)) % ", ".join(['%s']*len(accrued_entries)), tuple(accrued_entries))

View File

@ -14,27 +14,37 @@ frappe.ui.form.on('Blanket Order', {
refresh: function(frm) { refresh: function(frm) {
erpnext.hide_company(); erpnext.hide_company();
if (frm.doc.customer && frm.doc.docstatus === 1) { if (frm.doc.customer && frm.doc.docstatus === 1) {
frm.add_custom_button(__('View Orders'), function() { frm.add_custom_button(__("Sales Order"), function() {
frappe.set_route('List', 'Sales Order', {blanket_order: frm.doc.name});
});
frm.add_custom_button(__("Create Sales Order"), function(){
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_sales_order", method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
frm: frm frm: frm,
args: {
doctype: 'Sales Order'
}
}); });
}).addClass("btn-primary"); }, __('Create'));
frm.add_custom_button(__("Quotation"), function() {
frappe.model.open_mapped_doc({
method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
frm: frm,
args: {
doctype: 'Quotation'
}
});
}, __('Create'));
} }
if (frm.doc.supplier && frm.doc.docstatus === 1) { if (frm.doc.supplier && frm.doc.docstatus === 1) {
frm.add_custom_button(__('View Orders'), function() { frm.add_custom_button(__("Purchase Order"), function(){
frappe.set_route('List', 'Purchase Order', {blanket_order: frm.doc.name});
});
frm.add_custom_button(__("Create Purchase Order"), function(){
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_purchase_order", method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
frm: frm frm: frm,
args: {
doctype: 'Purchase Order'
}
}); });
}).addClass("btn-primary"); }, __('Create'));
} }
}, },

View File

@ -14,11 +14,19 @@ from erpnext.stock.doctype.item.item import get_item_defaults
class BlanketOrder(Document): class BlanketOrder(Document):
def validate(self): def validate(self):
self.validate_dates() self.validate_dates()
self.validate_duplicate_items()
def validate_dates(self): def validate_dates(self):
if getdate(self.from_date) > getdate(self.to_date): if getdate(self.from_date) > getdate(self.to_date):
frappe.throw(_("From date cannot be greater than To date")) frappe.throw(_("From date cannot be greater than To date"))
def validate_duplicate_items(self):
item_list = []
for item in self.items:
if item.item_code in item_list:
frappe.throw(_("Note: Item {0} added multiple times").format(frappe.bold(item.item_code)))
item_list.append(item.item_code)
def update_ordered_qty(self): def update_ordered_qty(self):
ref_doctype = "Sales Order" if self.blanket_order_type == "Selling" else "Purchase Order" ref_doctype = "Sales Order" if self.blanket_order_type == "Selling" else "Purchase Order"
item_ordered_qty = frappe._dict(frappe.db.sql(""" item_ordered_qty = frappe._dict(frappe.db.sql("""
@ -35,7 +43,14 @@ class BlanketOrder(Document):
d.db_set("ordered_qty", item_ordered_qty.get(d.item_code, 0)) d.db_set("ordered_qty", item_ordered_qty.get(d.item_code, 0))
@frappe.whitelist() @frappe.whitelist()
def make_sales_order(source_name): def make_order(source_name):
doctype = frappe.flags.args.doctype
def update_doc(source_doc, target_doc, source_parent):
if doctype == 'Quotation':
target_doc.quotation_to = 'Customer'
target_doc.party_name = source_doc.customer
def update_item(source, target, source_parent): def update_item(source, target, source_parent):
target_qty = source.get("qty") - source.get("ordered_qty") target_qty = source.get("qty") - source.get("ordered_qty")
target.qty = target_qty if not flt(target_qty) < 0 else 0 target.qty = target_qty if not flt(target_qty) < 0 else 0
@ -49,39 +64,11 @@ def make_sales_order(source_name):
target_doc = get_mapped_doc("Blanket Order", source_name, { target_doc = get_mapped_doc("Blanket Order", source_name, {
"Blanket Order": { "Blanket Order": {
"doctype": "Sales Order" "doctype": doctype,
"postprocess": update_doc
}, },
"Blanket Order Item": { "Blanket Order Item": {
"doctype": "Sales Order Item", "doctype": doctype + " Item",
"field_map": {
"rate": "blanket_order_rate",
"parent": "blanket_order"
},
"postprocess": update_item
}
})
return target_doc
@frappe.whitelist()
def make_purchase_order(source_name):
def update_item(source, target, source_parent):
target_qty = source.get("qty") - source.get("ordered_qty")
target.qty = target_qty if not flt(target_qty) < 0 else 0
item = get_item_defaults(target.item_code, source_parent.company)
if item:
target.item_name = item.get("item_name")
target.description = item.get("description")
target.uom = item.get("stock_uom")
target.warehouse = item.get("default_warehouse")
target.against_blanket_order = 1
target.blanket_order = source_name
target_doc = get_mapped_doc("Blanket Order", source_name, {
"Blanket Order": {
"doctype": "Purchase Order"
},
"Blanket Order Item": {
"doctype": "Purchase Order Item",
"field_map": { "field_map": {
"rate": "blanket_order_rate", "rate": "blanket_order_rate",
"parent": "blanket_order" "parent": "blanket_order"

View File

@ -6,7 +6,7 @@ def get_data():
'fieldname': 'blanket_order', 'fieldname': 'blanket_order',
'transactions': [ 'transactions': [
{ {
'items': ['Purchase Order', 'Sales Order'] 'items': ['Purchase Order', 'Sales Order', 'Quotation']
} }
] ]
} }

View File

@ -7,13 +7,17 @@ import frappe
import unittest import unittest
from frappe.utils import add_months, today from frappe.utils import add_months, today
from erpnext import get_company_currency from erpnext import get_company_currency
from .blanket_order import make_sales_order, make_purchase_order from .blanket_order import make_order
class TestBlanketOrder(unittest.TestCase): class TestBlanketOrder(unittest.TestCase):
def setUp(self):
frappe.flags.args = frappe._dict()
def test_sales_order_creation(self): def test_sales_order_creation(self):
bo = make_blanket_order(blanket_order_type="Selling") bo = make_blanket_order(blanket_order_type="Selling")
so = make_sales_order(bo.name) frappe.flags.args.doctype = 'Sales Order'
so = make_order(bo.name)
so.currency = get_company_currency(so.company) so.currency = get_company_currency(so.company)
so.delivery_date = today() so.delivery_date = today()
so.items[0].qty = 10 so.items[0].qty = 10
@ -29,7 +33,8 @@ class TestBlanketOrder(unittest.TestCase):
self.assertEqual(so.items[0].qty, bo.items[0].ordered_qty) self.assertEqual(so.items[0].qty, bo.items[0].ordered_qty)
# test the quantity # test the quantity
so1 = make_sales_order(bo.name) frappe.flags.args.doctype = 'Sales Order'
so1 = make_order(bo.name)
so1.currency = get_company_currency(so1.company) so1.currency = get_company_currency(so1.company)
self.assertEqual(so1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty)) self.assertEqual(so1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty))
@ -37,7 +42,8 @@ class TestBlanketOrder(unittest.TestCase):
def test_purchase_order_creation(self): def test_purchase_order_creation(self):
bo = make_blanket_order(blanket_order_type="Purchasing") bo = make_blanket_order(blanket_order_type="Purchasing")
po = make_purchase_order(bo.name) frappe.flags.args.doctype = 'Purchase Order'
po = make_order(bo.name)
po.currency = get_company_currency(po.company) po.currency = get_company_currency(po.company)
po.schedule_date = today() po.schedule_date = today()
po.items[0].qty = 10 po.items[0].qty = 10
@ -53,7 +59,8 @@ class TestBlanketOrder(unittest.TestCase):
self.assertEqual(po.items[0].qty, bo.items[0].ordered_qty) self.assertEqual(po.items[0].qty, bo.items[0].ordered_qty)
# test the quantity # test the quantity
po1 = make_sales_order(bo.name) frappe.flags.args.doctype = 'Purchase Order'
po1 = make_order(bo.name)
po1.currency = get_company_currency(po1.company) po1.currency = get_company_currency(po1.company)
self.assertEqual(po1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty)) self.assertEqual(po1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty))

View File

@ -29,7 +29,10 @@ frappe.ui.form.on("BOM", {
frm.set_query("item", function() { frm.set_query("item", function() {
return { return {
query: "erpnext.controllers.queries.item_query" query: "erpnext.controllers.queries.item_query",
filters: {
"doctype": "BOM"
}
}; };
}); });
@ -43,8 +46,7 @@ frappe.ui.form.on("BOM", {
frm.set_query("item_code", "items", function() { frm.set_query("item_code", "items", function() {
return { return {
query: "erpnext.controllers.queries.item_query", query: "erpnext.controllers.queries.item_query"
filters: [["Item", "name", "!=", cur_frm.doc.item]]
}; };
}); });
@ -120,22 +122,58 @@ frappe.ui.form.on("BOM", {
}); });
} }
} }
if (frm.doc.__onload && frm.doc.__onload["has_variants"]) {
frm.set_intro(__('This is a Template BOM and will be used to make the work order for {0} of the item {1}',
[
`<a class="variants-intro">variants</a>`,
`<a href="#Form/Item/${frm.doc.item}">${frm.doc.item}</a>`,
]), true);
frm.$wrapper.find(".variants-intro").on("click", () => {
frappe.set_route("List", "Item", {"variant_of": frm.doc.item});
});
}
}, },
make_work_order: function(frm) { make_work_order: function(frm) {
const fields = [{ const fields = [];
if (frm.doc.__onload && frm.doc.__onload["has_variants"]) {
fields.push({
fieldtype: 'Link',
label: __('Variant Item'),
fieldname: 'item',
options: "Item",
reqd: 1,
get_query: function() {
return {
query: "erpnext.controllers.queries.item_query",
filters: {
"variant_of": frm.doc.item
}
};
}
});
}
fields.push({
fieldtype: 'Float', fieldtype: 'Float',
label: __('Qty To Manufacture'), label: __('Qty To Manufacture'),
fieldname: 'qty', fieldname: 'qty',
reqd: 1, reqd: 1,
default: 1 default: 1
}]; });
frappe.prompt(fields, data => { frappe.prompt(fields, data => {
let item = data.item || frm.doc.item;
frappe.call({ frappe.call({
method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order",
args: { args: {
item: frm.doc.item, bom_no: frm.doc.name,
item: item,
qty: data.qty || 0.0, qty: data.qty || 0.0,
project: frm.doc.project project: frm.doc.project
}, },

View File

@ -59,6 +59,10 @@ class BOM(WebsiteGenerator):
self.name = name self.name = name
def onload(self):
super(BOM, self).onload()
if self.get("item") and cint(frappe.db.get_value("Item", self.item, "has_variants")):
self.set_onload("has_variants", True)
def validate(self): def validate(self):
self.route = frappe.scrub(self.name).replace('_', '-') self.route = frappe.scrub(self.name).replace('_', '-')
@ -114,10 +118,6 @@ class BOM(WebsiteGenerator):
child = self.append('operations', d) child = self.append('operations', d)
child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2)
def validate_rm_item(self, item):
if (item[0]['name'] in [it.item_code for it in self.items]) and item[0]['name'] == self.item:
frappe.throw(_("BOM #{0}: Raw material cannot be same as main Item").format(self.name))
def set_bom_material_details(self): def set_bom_material_details(self):
for item in self.get("items"): for item in self.get("items"):
self.validate_bom_currecny(item) self.validate_bom_currecny(item)
@ -147,7 +147,6 @@ class BOM(WebsiteGenerator):
args = json.loads(args) args = json.loads(args)
item = self.get_item_det(args['item_code']) item = self.get_item_det(args['item_code'])
self.validate_rm_item(item)
args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or '' args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or ''
args['transfer_for_manufacture'] = (cstr(args.get('include_item_in_manufacturing', '')) or args['transfer_for_manufacture'] = (cstr(args.get('include_item_in_manufacturing', '')) or
@ -498,6 +497,14 @@ class BOM(WebsiteGenerator):
self.scrap_material_cost = total_sm_cost self.scrap_material_cost = total_sm_cost
self.base_scrap_material_cost = base_total_sm_cost self.base_scrap_material_cost = base_total_sm_cost
def update_new_bom(self, old_bom, new_bom, rate):
for d in self.get("items"):
if d.bom_no != old_bom: continue
d.bom_no = new_bom
d.rate = rate
d.amount = (d.stock_qty or d.qty) * rate
def update_exploded_items(self): def update_exploded_items(self):
""" Update Flat BOM, following will be correct data""" """ Update Flat BOM, following will be correct data"""
self.get_exploded_items() self.get_exploded_items()
@ -827,6 +834,10 @@ def add_operations_cost(stock_entry, work_order=None, expense_account=None):
def get_bom_diff(bom1, bom2): def get_bom_diff(bom1, bom2):
from frappe.model import table_fields from frappe.model import table_fields
if bom1 == bom2:
frappe.throw(_("BOM 1 {0} and BOM 2 {1} should not be same")
.format(frappe.bold(bom1), frappe.bold(bom2)))
doc1 = frappe.get_doc('BOM', bom1) doc1 = frappe.get_doc('BOM', bom1)
doc2 = frappe.get_doc('BOM', bom2) doc2 = frappe.get_doc('BOM', bom2)

View File

@ -14,10 +14,13 @@ import click
class BOMUpdateTool(Document): class BOMUpdateTool(Document):
def replace_bom(self): def replace_bom(self):
self.validate_bom() self.validate_bom()
self.update_new_bom()
unit_cost = get_new_bom_unit_cost(self.new_bom)
self.update_new_bom(unit_cost)
frappe.cache().delete_key('bom_children') frappe.cache().delete_key('bom_children')
bom_list = self.get_parent_boms(self.new_bom) bom_list = self.get_parent_boms(self.new_bom)
updated_bom = []
with click.progressbar(bom_list) as bom_list: with click.progressbar(bom_list) as bom_list:
pass pass
for bom in bom_list: for bom in bom_list:
@ -26,7 +29,9 @@ class BOMUpdateTool(Document):
# this is only used for versioning and we do not want # this is only used for versioning and we do not want
# to make separate db calls by using load_doc_before_save # to make separate db calls by using load_doc_before_save
# which proves to be expensive while doing bulk replace # which proves to be expensive while doing bulk replace
bom_obj._doc_before_save = bom_obj.as_dict() bom_obj._doc_before_save = bom_obj
bom_obj.update_new_bom(self.current_bom, self.new_bom, unit_cost)
bom_obj.update_exploded_items()
bom_obj.calculate_cost() bom_obj.calculate_cost()
bom_obj.update_parent_cost() bom_obj.update_parent_cost()
bom_obj.db_update() bom_obj.db_update()
@ -43,14 +48,10 @@ class BOMUpdateTool(Document):
!= frappe.db.get_value("BOM", self.new_bom, "item"): != frappe.db.get_value("BOM", self.new_bom, "item"):
frappe.throw(_("The selected BOMs are not for the same item")) frappe.throw(_("The selected BOMs are not for the same item"))
def update_new_bom(self): def update_new_bom(self, unit_cost):
new_bom_unitcost = frappe.db.sql("""SELECT `total_cost`/`quantity`
FROM `tabBOM` WHERE name = %s""", self.new_bom)
new_bom_unitcost = flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0
frappe.db.sql("""update `tabBOM Item` set bom_no=%s, frappe.db.sql("""update `tabBOM Item` set bom_no=%s,
rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2 and parenttype='BOM'""", rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2 and parenttype='BOM'""",
(self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom)) (self.new_bom, unit_cost, unit_cost, self.current_bom))
def get_parent_boms(self, bom, bom_list=[]): def get_parent_boms(self, bom, bom_list=[]):
data = frappe.db.sql("""SELECT DISTINCT parent FROM `tabBOM Item` data = frappe.db.sql("""SELECT DISTINCT parent FROM `tabBOM Item`
@ -65,12 +66,18 @@ class BOMUpdateTool(Document):
return list(set(bom_list)) return list(set(bom_list))
def get_new_bom_unit_cost(bom):
new_bom_unitcost = frappe.db.sql("""SELECT `total_cost`/`quantity`
FROM `tabBOM` WHERE name = %s""", bom)
return flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0
@frappe.whitelist() @frappe.whitelist()
def enqueue_replace_bom(args): def enqueue_replace_bom(args):
if isinstance(args, string_types): if isinstance(args, string_types):
args = json.loads(args) args = json.loads(args)
frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args, timeout=4000) frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args, timeout=40000)
frappe.msgprint(_("Queued for replacing the BOM. It may take a few minutes.")) frappe.msgprint(_("Queued for replacing the BOM. It may take a few minutes."))
@frappe.whitelist() @frappe.whitelist()

View File

@ -20,7 +20,7 @@ frappe.ui.form.on('Job Card', {
} }
} }
if (frm.doc.docstatus == 0 && frm.doc.for_quantity > frm.doc.total_completed_qty if (frm.doc.docstatus == 0 && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
&& (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { && (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
frm.trigger("prepare_timer_buttons"); frm.trigger("prepare_timer_buttons");
} }
@ -59,10 +59,14 @@ frappe.ui.form.on('Job Card', {
let completed_time = frappe.datetime.now_datetime(); let completed_time = frappe.datetime.now_datetime();
frm.trigger("hide_timer"); frm.trigger("hide_timer");
if (frm.doc.for_quantity) {
frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'), frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'),
fieldname: 'qty', reqd: 1, default: frm.doc.for_quantity}, data => { fieldname: 'qty', reqd: 1, default: frm.doc.for_quantity}, data => {
frm.events.complete_job(frm, completed_time, data.qty); frm.events.complete_job(frm, completed_time, data.qty);
}, __("Enter Value"), __("Complete")); }, __("Enter Value"), __("Complete"));
} else {
frm.events.complete_job(frm, completed_time, 0);
}
}).addClass("btn-primary"); }).addClass("btn-primary");
} }
}, },

View File

@ -99,8 +99,7 @@
"fieldname": "for_quantity", "fieldname": "for_quantity",
"fieldtype": "Float", "fieldtype": "Float",
"in_list_view": 1, "in_list_view": 1,
"label": "Qty To Manufacture", "label": "Qty To Manufacture"
"reqd": 1
}, },
{ {
"fieldname": "wip_warehouse", "fieldname": "wip_warehouse",
@ -122,6 +121,7 @@
"options": "Employee" "options": "Employee"
}, },
{ {
"allow_bulk_edit": 1,
"fieldname": "time_logs", "fieldname": "time_logs",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Time Logs", "label": "Time Logs",
@ -290,7 +290,7 @@
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-12-03 13:08:57.926201", "modified": "2020-03-27 13:36:35.417502",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Job Card", "name": "Job Card",

View File

@ -191,12 +191,9 @@ class JobCard(Document):
if not self.time_logs: if not self.time_logs:
frappe.throw(_("Time logs are required for job card {0}").format(self.name)) frappe.throw(_("Time logs are required for job card {0}").format(self.name))
if self.total_completed_qty <= 0.0: if self.for_quantity and self.total_completed_qty != self.for_quantity:
frappe.throw(_("Total completed qty must be greater than zero")) frappe.throw(_("The total completed qty({0}) must be equal to qty to manufacture({1})"
.format(frappe.bold(self.total_completed_qty),frappe.bold(self.for_quantity))))
if self.total_completed_qty != self.for_quantity:
frappe.throw(_("The total completed qty({0}) must be equal to qty to manufacture({1})")
.format(frappe.bold(self.total_completed_qty),frappe.bold(self.for_quantity)))
def update_work_order(self): def update_work_order(self):
if not self.work_order: if not self.work_order:
@ -205,27 +202,34 @@ class JobCard(Document):
for_quantity, time_in_mins = 0, 0 for_quantity, time_in_mins = 0, 0
from_time_list, to_time_list = [], [] from_time_list, to_time_list = [], []
for d in frappe.get_all('Job Card',
filters = {'docstatus': 1, 'operation_id': self.operation_id}):
doc = frappe.get_doc('Job Card', d.name)
for_quantity += doc.total_completed_qty data = frappe.get_all('Job Card',
time_in_mins += doc.total_time_in_mins fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
for time_log in doc.time_logs: filters = {"docstatus": 1, "work_order": self.work_order,
if time_log.from_time: "workstation": self.workstation, "operation": self.operation})
from_time_list.append(time_log.from_time)
if time_log.to_time: if data and len(data) > 0:
to_time_list.append(time_log.to_time) for_quantity = data[0].completed_qty
time_in_mins = data[0].time_in_mins
if for_quantity: if for_quantity:
time_data = frappe.db.sql("""
SELECT
min(from_time) as start_time, max(to_time) as end_time
FROM `tabJob Card` jc, `tabJob Card Time Log` jctl
WHERE
jctl.parent = jc.name and jc.work_order = %s
and jc.workstation = %s and jc.operation = %s and jc.docstatus = 1
""", (self.work_order, self.workstation, self.operation), as_dict=1)
wo = frappe.get_doc('Work Order', self.work_order) wo = frappe.get_doc('Work Order', self.work_order)
for data in wo.operations: for data in wo.operations:
if data.name == self.operation_id: if data.workstation == self.workstation and data.operation == self.operation:
data.completed_qty = for_quantity data.completed_qty = for_quantity
data.actual_operation_time = time_in_mins data.actual_operation_time = time_in_mins
data.actual_start_time = min(from_time_list) if from_time_list else None data.actual_start_time = time_data[0].start_time if time_data else None
data.actual_end_time = max(to_time_list) if to_time_list else None data.actual_end_time = time_data[0].end_time if time_data else None
wo.flags.ignore_validate_update_after_submit = True wo.flags.ignore_validate_update_after_submit = True
wo.update_operation_status() wo.update_operation_status()

View File

@ -144,7 +144,7 @@ class ProductionPlan(Document):
item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code)) item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code))
items = frappe.db.sql("""select distinct parent, name, item_code, warehouse, description, items = frappe.db.sql("""select distinct parent, name, item_code, warehouse, description,
(qty - ordered_qty) as pending_qty (qty - ordered_qty) * conversion_factor as pending_qty
from `tabMaterial Request Item` mr_item from `tabMaterial Request Item` mr_item
where parent in (%s) and docstatus = 1 and qty > ordered_qty where parent in (%s) and docstatus = 1 and qty > ordered_qty
and exists (select name from `tabBOM` bom where bom.item=mr_item.item_code and exists (select name from `tabBOM` bom where bom.item=mr_item.item_code

View File

@ -14,6 +14,7 @@ from erpnext.stock.utils import get_bin
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
class TestWorkOrder(unittest.TestCase): class TestWorkOrder(unittest.TestCase):
def setUp(self): def setUp(self):
@ -82,6 +83,37 @@ class TestWorkOrder(unittest.TestCase):
wo_order.set_work_order_operations() wo_order.set_work_order_operations()
self.assertEqual(wo_order.planned_operating_cost, cost*2) self.assertEqual(wo_order.planned_operating_cost, cost*2)
def test_resered_qty_for_partial_completion(self):
item = "_Test Item"
warehouse = create_warehouse("Test Warehouse for reserved_qty - _TC")
bin1_at_start = get_bin(item, warehouse)
# reset to correct value
bin1_at_start.update_reserved_qty_for_production()
wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2,
source_warehouse=warehouse, skip_transfer=1)
bin1_on_submit = get_bin(item, warehouse)
# reserved qty for production is updated
self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2,
cint(bin1_on_submit.reserved_qty_for_production))
test_stock_entry.make_stock_entry(item_code="_Test Item",
target=warehouse, qty=100, basic_rate=100)
test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
target=warehouse, qty=100, basic_rate=100)
s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 1))
s.submit()
bin1_at_completion = get_bin(item, warehouse)
self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production),
cint(bin1_on_submit.reserved_qty_for_production) - 1)
def test_production_item(self): def test_production_item(self):
wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True) wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True)
frappe.db.set_value("Item", "_Test FG Item", "end_of_life", "2000-1-1") frappe.db.set_value("Item", "_Test FG Item", "end_of_life", "2000-1-1")
@ -404,7 +436,7 @@ def make_wo_order_test_record(**args):
wo_order.company = args.company or "_Test Company" wo_order.company = args.company or "_Test Company"
wo_order.stock_uom = args.stock_uom or "_Test UOM" wo_order.stock_uom = args.stock_uom or "_Test UOM"
wo_order.use_multi_level_bom=0 wo_order.use_multi_level_bom=0
wo_order.skip_transfer=1 wo_order.skip_transfer=args.skip_transfer or 0
wo_order.get_items_and_operations_from_bom() wo_order.get_items_and_operations_from_bom()
wo_order.sales_order = args.sales_order or None wo_order.sales_order = args.sales_order or None
wo_order.planned_start_date = args.planned_start_date or now() wo_order.planned_start_date = args.planned_start_date or now()

View File

@ -279,7 +279,7 @@ class WorkOrder(Document):
if enable_capacity_planning and job_card_doc: if enable_capacity_planning and job_card_doc:
row.planned_start_time = job_card_doc.time_logs[-1].from_time row.planned_start_time = job_card_doc.time_logs[-1].from_time
row.planned_end_time = job_card_doc.time_logs[-1].to_time row.planned_end_time = job_card_doc.time_logs[-1].to_time
print(row.planned_start_time, original_start_time, plan_days)
if date_diff(row.planned_start_time, original_start_time) > plan_days: if date_diff(row.planned_start_time, original_start_time) > plan_days:
frappe.message_log.pop() frappe.message_log.pop()
frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.") frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.")
@ -314,7 +314,7 @@ class WorkOrder(Document):
stock_entry = frappe.db.sql("""select name from `tabStock Entry` stock_entry = frappe.db.sql("""select name from `tabStock Entry`
where work_order = %s and docstatus = 1""", self.name) where work_order = %s and docstatus = 1""", self.name)
if stock_entry: if stock_entry:
frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(stock_entry[0][0])) frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(frappe.utils.get_link_to_form('Stock Entry', stock_entry[0][0])))
def update_planned_qty(self): def update_planned_qty(self):
update_bin_qty(self.production_item, self.fg_warehouse, { update_bin_qty(self.production_item, self.fg_warehouse, {
@ -468,6 +468,9 @@ class WorkOrder(Document):
update bin reserved_qty_for_production update bin reserved_qty_for_production
called from Stock Entry for production, after submit, cancel called from Stock Entry for production, after submit, cancel
''' '''
# calculate consumed qty based on submitted stock entries
self.update_consumed_qty_for_required_items()
if self.docstatus==1: if self.docstatus==1:
# calculate transferred qty based on submitted stock entries # calculate transferred qty based on submitted stock entries
self.update_transaferred_qty_for_required_items() self.update_transaferred_qty_for_required_items()
@ -475,9 +478,6 @@ class WorkOrder(Document):
# update in bin # update in bin
self.update_reserved_qty_for_production() self.update_reserved_qty_for_production()
# calculate consumed qty based on submitted stock entries
self.update_consumed_qty_for_required_items()
def update_reserved_qty_for_production(self, items=None): def update_reserved_qty_for_production(self, items=None):
'''update reserved_qty_for_production in bins''' '''update reserved_qty_for_production in bins'''
for d in self.required_items: for d in self.required_items:
@ -552,24 +552,33 @@ class WorkOrder(Document):
d.db_set('transferred_qty', flt(transferred_qty), update_modified = False) d.db_set('transferred_qty', flt(transferred_qty), update_modified = False)
def update_consumed_qty_for_required_items(self): def update_consumed_qty_for_required_items(self):
'''update consumed qty from submitted stock entries for that item against '''
the work order''' Update consumed qty from submitted stock entries
against a work order for each stock item
'''
for d in self.required_items: for item in self.required_items:
consumed_qty = frappe.db.sql('''select sum(qty) consumed_qty = frappe.db.sql('''
from `tabStock Entry` entry, `tabStock Entry Detail` detail SELECT
where SUM(qty)
FROM
`tabStock Entry` entry,
`tabStock Entry Detail` detail
WHERE
entry.work_order = %(name)s entry.work_order = %(name)s
and (entry.purpose = "Material Consumption for Manufacture" AND (entry.purpose = "Material Consumption for Manufacture"
or entry.purpose = "Manufacture") OR entry.purpose = "Manufacture")
and entry.docstatus = 1 AND entry.docstatus = 1
and detail.parent = entry.name AND detail.parent = entry.name
and (detail.item_code = %(item)s or detail.original_item = %(item)s)''', { AND detail.s_warehouse IS NOT null
AND (detail.item_code = %(item)s
OR detail.original_item = %(item)s)
''', {
'name': self.name, 'name': self.name,
'item': d.item_code 'item': item.item_code
})[0][0] })[0][0]
d.db_set('consumed_qty', flt(consumed_qty), update_modified = False) item.db_set('consumed_qty', flt(consumed_qty), update_modified=False)
def make_bom(self): def make_bom(self):
data = frappe.db.sql(""" select sed.item_code, sed.qty, sed.s_warehouse data = frappe.db.sql(""" select sed.item_code, sed.qty, sed.s_warehouse
@ -648,7 +657,7 @@ def get_item_details(item, project = None):
return res return res
@frappe.whitelist() @frappe.whitelist()
def make_work_order(item, qty=0, project=None): def make_work_order(bom_no, item, qty=0, project=None):
if not frappe.has_permission("Work Order", "write"): if not frappe.has_permission("Work Order", "write"):
frappe.throw(_("Not permitted"), frappe.PermissionError) frappe.throw(_("Not permitted"), frappe.PermissionError)
@ -657,6 +666,7 @@ def make_work_order(item, qty=0, project=None):
wo_doc = frappe.new_doc("Work Order") wo_doc = frappe.new_doc("Work Order")
wo_doc.production_item = item wo_doc.production_item = item
wo_doc.update(item_details) wo_doc.update(item_details)
wo_doc.bom_no = bom_no
if flt(qty) > 0: if flt(qty) > 0:
wo_doc.qty = flt(qty) wo_doc.qty = flt(qty)

View File

@ -22,7 +22,14 @@ erpnext.BOMComparisonTool = class BOMComparisonTool {
fieldname: 'name1', fieldname: 'name1',
fieldtype: 'Link', fieldtype: 'Link',
options: 'BOM', options: 'BOM',
change: () => this.fetch_and_render() change: () => this.fetch_and_render(),
get_query: () => {
return {
filters: {
"name": ["not in", [this.form.get_value("name2") || ""]]
}
}
}
}, },
{ {
fieldtype: 'Column Break' fieldtype: 'Column Break'
@ -32,7 +39,14 @@ erpnext.BOMComparisonTool = class BOMComparisonTool {
fieldname: 'name2', fieldname: 'name2',
fieldtype: 'Link', fieldtype: 'Link',
options: 'BOM', options: 'BOM',
change: () => this.fetch_and_render() change: () => this.fetch_and_render(),
get_query: () => {
return {
filters: {
"name": ["not in", [this.form.get_value("name1") || ""]]
}
}
}
}, },
{ {
fieldtype: 'Section Break' fieldtype: 'Section Break'

View File

@ -581,7 +581,7 @@ erpnext.patches.v11_0.rename_bom_wo_fields
erpnext.patches.v12_0.set_default_homepage_type erpnext.patches.v12_0.set_default_homepage_type
erpnext.patches.v11_0.rename_additional_salary_component_additional_salary erpnext.patches.v11_0.rename_additional_salary_component_additional_salary
erpnext.patches.v11_0.renamed_from_to_fields_in_project erpnext.patches.v11_0.renamed_from_to_fields_in_project
erpnext.patches.v11_0.add_permissions_in_gst_settings erpnext.patches.v11_0.add_permissions_in_gst_settings #2020-04-04
erpnext.patches.v11_1.setup_guardian_role erpnext.patches.v11_1.setup_guardian_role
execute:frappe.delete_doc('DocType', 'Notification Control') execute:frappe.delete_doc('DocType', 'Notification Control')
erpnext.patches.v12_0.set_gst_category erpnext.patches.v12_0.set_gst_category
@ -630,6 +630,7 @@ execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_link')
execute:frappe.reload_doc('desk', 'doctype', 'dashboard') execute:frappe.reload_doc('desk', 'doctype', 'dashboard')
execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_source') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_source')
execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart')
execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field')
erpnext.patches.v12_0.add_default_dashboards erpnext.patches.v12_0.add_default_dashboards
erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.remove_bank_remittance_custom_fields
erpnext.patches.v12_0.generate_leave_ledger_entries erpnext.patches.v12_0.generate_leave_ledger_entries
@ -658,4 +659,6 @@ erpnext.patches.v12_0.set_permission_einvoicing
erpnext.patches.v12_0.set_published_in_hub_tracked_item erpnext.patches.v12_0.set_published_in_hub_tracked_item
erpnext.patches.v12_0.set_job_offer_applicant_email erpnext.patches.v12_0.set_job_offer_applicant_email
erpnext.patches.v12_0.create_irs_1099_field_united_states erpnext.patches.v12_0.create_irs_1099_field_united_states
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22
erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom

View File

@ -1,12 +1,9 @@
import frappe import frappe
from frappe.permissions import add_permission, update_permission_property from erpnext.regional.india.setup import add_permissions
def execute(): def execute():
company = frappe.get_all('Company', filters = {'country': 'India'}) company = frappe.get_all('Company', filters = {'country': 'India'})
if not company: if not company:
return return
for doctype in ('GST HSN Code', 'GST Settings'): add_permissions()
add_permission(doctype, 'Accounts Manager', 0)
update_permission_property(doctype, 'Accounts Manager', 0, 'write', 1)
update_permission_property(doctype, 'Accounts Manager', 0, 'create', 1)

View File

@ -0,0 +1,15 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc('accounts', 'doctype', 'bank', force=1)
if frappe.db.table_exists('Bank') and frappe.db.table_exists('Bank Account'):
frappe.db.sql("""
UPDATE `tabBank` b, `tabBank Account` ba
SET b.swift_number = ba.swift_number, b.branch_code = ba.branch_code
WHERE b.name = ba.bank
""")
frappe.reload_doc('accounts', 'doctype', 'bank_account')
frappe.reload_doc('accounts', 'doctype', 'payment_request')

View File

@ -0,0 +1,30 @@
from __future__ import unicode_literals
import frappe
def execute():
purchase_receipts = frappe.db.sql("""
SELECT
parent from `tabPurchase Receipt Item`
WHERE
material_request is not null
AND docstatus=1
""",as_dict=1)
purchase_receipts = set([d.parent for d in purchase_receipts])
for pr in purchase_receipts:
doc = frappe.get_doc("Purchase Receipt", pr)
doc.status_updater = [
{
'source_dt': 'Purchase Receipt Item',
'target_dt': 'Material Request Item',
'join_field': 'material_request_item',
'target_field': 'received_qty',
'target_parent_dt': 'Material Request',
'target_parent_field': 'per_received',
'target_ref_field': 'stock_qty',
'source_field': 'stock_qty',
'percent_join_field': 'material_request'
}
]
doc.update_qty()

View File

@ -18,7 +18,7 @@ frappe.ui.form.on("Project", {
}; };
}, },
onload: function (frm) { onload: function (frm) {
var so = frappe.meta.get_docfield("Project", "sales_order"); var so = frm.get_docfield("Project", "sales_order");
so.get_route_options_for_new_doc = function (field) { so.get_route_options_for_new_doc = function (field) {
if (frm.is_new()) return; if (frm.is_new()) return;
return { return {

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"autoname": "TASK-.YYYY.-.#####", "autoname": "TASK-.YYYY.-.#####",
"creation": "2013-01-29 19:25:50", "creation": "2013-01-29 19:25:50",
@ -200,7 +201,6 @@
{ {
"fieldname": "description", "fieldname": "description",
"fieldtype": "Text Editor", "fieldtype": "Text Editor",
"in_preview": 1,
"label": "Task Description", "label": "Task Description",
"oldfieldname": "description", "oldfieldname": "description",
"oldfieldtype": "Text Editor", "oldfieldtype": "Text Editor",
@ -361,11 +361,14 @@
], ],
"icon": "fa fa-check", "icon": "fa fa-check",
"idx": 1, "idx": 1,
"is_tree": 1,
"links": [],
"max_attachments": 5, "max_attachments": 5,
"modified": "2019-09-10 13:46:24.631754", "modified": "2020-03-18 18:08:44.153211",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Projects", "module": "Projects",
"name": "Task", "name": "Task",
"nsm_parent_field": "parent_task",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@ -6,7 +6,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
apply_pricing_rule_on_item: function(item){ apply_pricing_rule_on_item: function(item){
let effective_item_rate = item.price_list_rate; let effective_item_rate = item.price_list_rate;
if (item.parenttype === "Sales Order" && item.blanket_order_rate) { if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
effective_item_rate = item.blanket_order_rate; effective_item_rate = item.blanket_order_rate;
} }
if(item.margin_type == "Percentage"){ if(item.margin_type == "Percentage"){

View File

@ -4,7 +4,7 @@
erpnext.TransactionController = erpnext.taxes_and_totals.extend({ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
setup: function() { setup: function() {
this._super(); this._super();
frappe.flags.hide_serial_batch_dialog = false; frappe.flags.hide_serial_batch_dialog = true;
frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) { frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn); var item = frappe.get_doc(cdt, cdn);
var has_margin_field = frappe.meta.has_field(cdt, 'margin_type'); var has_margin_field = frappe.meta.has_field(cdt, 'margin_type');
@ -165,6 +165,16 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
return (doc.rule_applied) ? "green" : "red"; return (doc.rule_applied) ? "green" : "red";
}); });
} }
let batch_no_field = this.frm.get_docfield("items", "batch_no");
if (batch_no_field) {
batch_no_field.get_route_options_for_new_doc = function(row) {
return {
"item": row.doc.item_code
}
};
}
}, },
onload: function() { onload: function() {
var me = this; var me = this;
@ -352,12 +362,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
['serial_no', 'batch_no', 'barcode'].forEach(field => { ['serial_no', 'batch_no', 'barcode'].forEach(field => {
if (data[field] && frappe.meta.has_field(row_to_modify.doctype, field)) { if (data[field] && frappe.meta.has_field(row_to_modify.doctype, field)) {
let value = (row_to_modify[field] && field === "serial_no")
? row_to_modify[field] + '\n' + data[field] : data[field];
frappe.model.set_value(row_to_modify.doctype, frappe.model.set_value(row_to_modify.doctype,
row_to_modify.name, field, data[field]); row_to_modify.name, field, value);
} }
}); });
scan_barcode_field.set_value(''); scan_barcode_field.set_value('');
refresh_field("items");
}); });
} }
return false; return false;
@ -519,6 +534,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
} }
}, },
() => me.toggle_conversion_factor(item), () => me.toggle_conversion_factor(item),
() => {
if (show_batch_dialog)
return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
.then((r) => {
if(r.message.has_batch_no || r.message.has_serial_no) {
frappe.flags.hide_serial_batch_dialog = false;
}
});
},
() => { () => {
if(show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) { if(show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
@ -528,7 +552,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
erpnext.show_serial_batch_selector(me.frm, d, (item) => { erpnext.show_serial_batch_selector(me.frm, d, (item) => {
me.frm.script_manager.trigger('qty', item.doctype, item.name); me.frm.script_manager.trigger('qty', item.doctype, item.name);
}); if (!me.frm.doc.set_warehouse)
me.frm.script_manager.trigger('warehouse', item.doctype, item.name);
}, undefined, !frappe.flags.hide_serial_batch_dialog);
} }
}, },
() => me.conversion_factor(doc, cdt, cdn, true), () => me.conversion_factor(doc, cdt, cdn, true),

View File

@ -29,7 +29,7 @@ class Quiz {
this.questions.push(question) this.questions.push(question)
this.wrapper.appendChild(question_wrapper); this.wrapper.appendChild(question_wrapper);
}) })
if (data.activity.is_complete) { if (data.activity && data.activity.is_complete) {
this.disable() this.disable()
let indicator = 'red' let indicator = 'red'
let message = 'Your are not allowed to attempt the quiz again.' let message = 'Your are not allowed to attempt the quiz again.'

View File

@ -453,7 +453,8 @@ erpnext.utils.update_child_items = function(opts) {
fields: [{ fields: [{
fieldtype:'Data', fieldtype:'Data',
fieldname:"docname", fieldname:"docname",
hidden: 0, read_only: 1,
hidden: 1,
}, { }, {
fieldtype:'Link', fieldtype:'Link',
fieldname:"item_code", fieldname:"item_code",

View File

@ -1,11 +1,13 @@
frappe.provide('frappe.ui.form'); frappe.provide('frappe.ui.form');
erpnext.doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset", let default_dimensions = {};
let doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
"Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Shipping Rule", "Loyalty Program", "Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Shipping Rule", "Loyalty Program",
"Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool",
"Subscription", "Purchase Order", "Journal Entry", "Material Request", "Purchase Receipt", "Landed Cost Item", "Asset"]; "Subscription", "Purchase Order", "Journal Entry", "Material Request", "Purchase Receipt", "Landed Cost Item", "Asset"];
erpnext.child_docs = ["Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", let child_docs = ["Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account",
"Material Request Item", "Delivery Note Item", "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction",
"Landed Cost Item", "Asset Value Adjustment", "Opening Invoice Creation Tool Item", "Subscription Plan"]; "Landed Cost Item", "Asset Value Adjustment", "Opening Invoice Creation Tool Item", "Subscription Plan"];
@ -13,11 +15,11 @@ frappe.call({
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimension_filters", method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimension_filters",
callback: function(r) { callback: function(r) {
erpnext.dimension_filters = r.message[0]; erpnext.dimension_filters = r.message[0];
erpnext.default_dimensions = r.message[1]; default_dimensions = r.message[1];
} }
}); });
erpnext.doctypes_with_dimensions.forEach((doctype) => { doctypes_with_dimensions.forEach((doctype) => {
frappe.ui.form.on(doctype, { frappe.ui.form.on(doctype, {
onload: function(frm) { onload: function(frm) {
erpnext.dimension_filters.forEach((dimension) => { erpnext.dimension_filters.forEach((dimension) => {
@ -27,41 +29,40 @@ erpnext.doctypes_with_dimensions.forEach((doctype) => {
"is_group": 0 "is_group": 0
}); });
} }
if (Object.keys(erpnext.default_dimensions).length > 0) {
if (frappe.meta.has_field(doctype, dimension['fieldname'])) {
if (frm.is_new() && frappe.meta.has_field(doctype, 'company') && frm.doc.company) {
frm.set_value(dimension['fieldname'], erpnext.default_dimensions[frm.doc.company][dimension['document_type']]);
}
}
if (frm.doc.items && frm.doc.items.length && frm.doc.docstatus === 0
&& (!frm.doc.items[0][dimension['fieldname']])) {
frm.doc.items[0][dimension['fieldname']] = erpnext.default_dimensions[frm.doc.company][dimension['document_type']];
}
if (frm.doc.accounts && frm.doc.accounts.length && frm.doc.docstatus === 0
&& (!frm.doc.items[0][dimension['fieldname']])) {
frm.doc.accounts[0][dimension['fieldname']] = erpnext.default_dimensions[frm.doc.company][dimension['document_type']];
}
}
}); });
}); });
}, },
company: function(frm) { company: function(frm) {
if(frm.doc.company && (Object.keys(erpnext.default_dimensions).length > 0)) { if(frm.doc.company && (Object.keys(default_dimensions || {}).length > 0)
&& default_dimensions[frm.doc.company]) {
frm.trigger('update_dimension');
}
},
update_dimension: function(frm) {
erpnext.dimension_filters.forEach((dimension) => { erpnext.dimension_filters.forEach((dimension) => {
if (frm.is_new()) {
if (frm.doc.company && Object.keys(default_dimensions || {}).length > 0
&& default_dimensions[frm.doc.company]) {
if (frappe.meta.has_field(doctype, dimension['fieldname'])) { if (frappe.meta.has_field(doctype, dimension['fieldname'])) {
frm.set_value(dimension['fieldname'], erpnext.default_dimensions[frm.doc.company][dimension['document_type']]); frm.set_value(dimension['fieldname'],
default_dimensions[frm.doc.company][dimension['document_type']]);
}
$.each(frm.doc.items || frm.doc.accounts || [], function(i, row) {
frappe.model.set_value(row.doctype, row.name, dimension['fieldname'],
default_dimensions[frm.doc.company][dimension['document_type']])
});
}
} }
}); });
} }
},
}); });
}); });
erpnext.child_docs.forEach((doctype) => { child_docs.forEach((doctype) => {
frappe.ui.form.on(doctype, { frappe.ui.form.on(doctype, {
items_add: function(frm, cdt, cdn) { items_add: function(frm, cdt, cdn) {
erpnext.dimension_filters.forEach((dimension) => { erpnext.dimension_filters.forEach((dimension) => {
@ -77,14 +78,6 @@ erpnext.child_docs.forEach((doctype) => {
}); });
}, },
company: function(frm) {
if(frm.doc.company) {
erpnext.dimension_filters.forEach((dimension) => {
frm.set_value(dimension['fieldname'], erpnext.default_dimensions[frm.doc.company][dimension['document_type']]);
});
}
},
items_add: function(frm, cdt, cdn) { items_add: function(frm, cdt, cdn) {
erpnext.dimension_filters.forEach((dimension) => { erpnext.dimension_filters.forEach((dimension) => {
var row = frappe.get_doc(cdt, cdn); var row = frappe.get_doc(cdt, cdn);

View File

@ -8,12 +8,19 @@ frappe.ui.form.ItemQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({
render_dialog: function() { render_dialog: function() {
this.mandatory = this.get_variant_fields().concat(this.mandatory); this.mandatory = this.get_variant_fields().concat(this.mandatory);
this.mandatory = this.mandatory.concat(this.get_attributes_fields()); this.mandatory = this.mandatory.concat(this.get_attributes_fields());
this.check_naming_series_based_on();
this._super(); this._super();
this.init_post_render_dialog_operations(); this.init_post_render_dialog_operations();
this.preset_fields_for_template(); this.preset_fields_for_template();
this.dialog.$wrapper.find('.edit-full').text(__('Edit in full page for more options like assets, serial nos, batches etc.')) this.dialog.$wrapper.find('.edit-full').text(__('Edit in full page for more options like assets, serial nos, batches etc.'))
}, },
check_naming_series_based_on: function() {
if (frappe.defaults.get_default("item_naming_by") === "Naming Series") {
this.mandatory = this.mandatory.filter(d => d.fieldname !== "item_code");
}
},
init_post_render_dialog_operations: function() { init_post_render_dialog_operations: function() {
this.dialog.fields_dict.attribute_html.$wrapper.append(frappe.render_template("item_quick_entry")); this.dialog.fields_dict.attribute_html.$wrapper.append(frappe.render_template("item_quick_entry"));
this.init_for_create_variant_trigger(); this.init_for_create_variant_trigger();

Some files were not shown because too many files have changed in this diff Show More