Merge branch 'version-12-hotfix' into expense-claim-fix-v12

This commit is contained in:
Nabin Hait
2020-04-06 12:14:27 +05:30
committed by GitHub
147 changed files with 3587 additions and 7084 deletions

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 18:26:03.992861",
"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,15 +102,15 @@ 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 = {}
parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \ parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \
["account_name", "account_number"]) ["account_name", "account_number"])
filters = { filters = {
"company": ["in", descendants], "company": ["in", descendants],
"account_name": parent_acc_name, "account_name": parent_acc_name,
} }
if parent_acc_number: if parent_acc_number:
filters["account_number"] = parent_acc_number filters["account_number"] = parent_acc_number

View File

@@ -2417,29 +2417,26 @@
"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)": { "Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": {
"is_group": 1, "account_number": "4850"
"Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": { },
"account_number": "4850" "Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (bei Buchgewinn)": {
}, "account_number": "4851"
"Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (bei Buchgewinn)": { },
"account_number": "4851" "Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (inl\u00e4ndische Kap.Ges., bei Buchgewinn)": {
}, "account_number": "4852"
"Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (inl\u00e4ndische Kap.Ges., bei Buchgewinn)": { },
"account_number": "4852" "Anlagenabg\u00e4nge Sachanlagen (Restbuchwert bei Buchvergewinn)": {
}, "account_number": "4855"
"Anlagenabg\u00e4nge Sachanlagen (Restbuchwert bei Buchvergewinn)": { },
"account_number": "4855" "Anlagenabg\u00e4nge immaterielle VG (Restbuchwert bei Buchgewinn)": {
}, "account_number": "4856"
"Anlagenabg\u00e4nge immaterielle VG (Restbuchwert bei Buchgewinn)": { },
"account_number": "4856" "Anlagenabg\u00e4nge Finanzanlagen (Restbuchwert bei Buchgewinn)": {
}, "account_number": "4857"
"Anlagenabg\u00e4nge Finanzanlagen (Restbuchwert bei Buchgewinn)": { },
"account_number": "4857" "Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": {
}, "account_number": "4858"
"Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": {
"account_number": "4858"
}
}, },
"Ertr\u00e4ge aus Zuschreibungen des Sachanlageverm\u00f6gens": { "Ertr\u00e4ge aus Zuschreibungen des Sachanlageverm\u00f6gens": {
"account_number": "4910", "account_number": "4910",
@@ -2562,20 +2559,17 @@
"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)": { "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": {
"is_group": 1, "account_number": "4630"
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": { },
"account_number": "4630" "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt": {
}, "account_number": "4637"
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt": { },
"account_number": "4637" "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternnehmens ohne USt (Telefon-Nutzung)": {
}, "account_number": "4638"
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternnehmens ohne USt (Telefon-Nutzung)": { },
"account_number": "4638" "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": {
}, "account_number": "4639"
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": {
"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,
@@ -2613,14 +2607,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)": { "Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": {
"is_group": 1, "account_number": "4690"
"Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": { },
"account_number": "4690" "Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": {
}, "account_number": "4695"
"Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": {
"account_number": "4695"
}
}, },
"Au\u00dferordentliche Ertr\u00e4ge (Gruppe)": { "Au\u00dferordentliche Ertr\u00e4ge (Gruppe)": {
"is_group": 1, "is_group": 1,
@@ -2630,41 +2621,35 @@
"Au\u00dferordentliche Ertr\u00e4ge finanzwirksam": { "Au\u00dferordentliche Ertr\u00e4ge finanzwirksam": {
"account_number": "7401" "account_number": "7401"
}, },
"Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam (Gruppe)": { "Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": {
"is_group": 1, "account_number": "7450"
"Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": {
"account_number": "7450"
},
"Ertr\u00e4ge durch Verschmelzung und Umwandlung": {
"account_number": "7451"
},
"Ertr\u00e4ge durch den Verkauf von bedeutenden Beteiligungen": {
"account_number": "7452"
},
"Ert\u00e4ge durch den Verkauf von bedeutenden Grundst\u00fccken": {
"account_number": "7453"
},
"Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": {
"account_number": "7454"
}
}, },
"Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften (Gruppe)": { "Ertr\u00e4ge durch Verschmelzung und Umwandlung": {
"is_group": 1, "account_number": "7451"
"Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": { },
"account_number": "7460" "Ertr\u00e4ge durch den Verkauf von bedeutenden Beteiligungen": {
}, "account_number": "7452"
"Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Sachanlageverm\u00f6gen": { },
"account_number": "7461" "Ert\u00e4ge durch den Verkauf von bedeutenden Grundst\u00fccken": {
}, "account_number": "7453"
"Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Finanzanlageverm\u00f6gen": { },
"account_number": "7462" "Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": {
}, "account_number": "7454"
"Au\u00dferordentliche Ertr\u00e4ge: Wertpapiere im Umlaufverm\u00f6gen": { },
"account_number": "7463" "Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": {
}, "account_number": "7460"
"Au\u00dferordentliche Ertr\u00e4ge: latente Steuern": { },
"account_number": "7464" "Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Sachanlageverm\u00f6gen": {
} "account_number": "7461"
},
"Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Finanzanlageverm\u00f6gen": {
"account_number": "7462"
},
"Au\u00dferordentliche Ertr\u00e4ge: Wertpapiere im Umlaufverm\u00f6gen": {
"account_number": "7463"
},
"Au\u00dferordentliche Ertr\u00e4ge: latente Steuern": {
"account_number": "7464"
} }
} }
}, },
@@ -2702,40 +2687,43 @@
}, },
"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)": {
"is_group": 1,
"Gew. Skonti": {
"account_number": "4730"
}, },
"Gew\u00e4hrte Skonti (Gruppe)": { "Gew. Skonti 7 % USt": {
"is_group": 1, "account_number": "4731"
"Gew. Skonti": {
"account_number": "4730"
},
"Gew. Skonti 7 % USt": {
"account_number": "4731"
},
"Gew. Skonti 19 % USt": {
"account_number": "4736"
},
"Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": {
"account_number": "4738"
},
"Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": {
"account_number": "4741"
},
"Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": {
"account_number": "4742"
},
"Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": {
"account_number": "4743"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": {
"account_number": "4745"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": {
"account_number": "4746"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": {
"account_number": "4748"
}
}, },
"Gew. Skonti 19 % USt": {
"account_number": "4736"
},
"Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": {
"account_number": "4738"
},
"Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": {
"account_number": "4741"
},
"Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": {
"account_number": "4742"
},
"Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": {
"account_number": "4743"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": {
"account_number": "4745"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": {
"account_number": "4746"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": {
"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"
}, },
@@ -2848,103 +2836,79 @@
"account_number": "6398" "account_number": "6398"
} }
}, },
"Versicherungen (Gruppe)": { "Versicherungen": {
"is_group": 1, "account_number": "6400"
"Versicherungen": { },
"account_number": "6400" "Versicherungen f. Geb\u00e4ude, die zum Betriebsverm\u00f6gen geh\u00f6ren": {
}, "account_number": "6405"
"Versicherungen f. Geb\u00e4ude, die zum Betriebsverm\u00f6gen geh\u00f6ren": { },
"account_number": "6405" "Netto-Pr\u00e4mie f. R\u00fcckdeckung k\u00fcnftiger Versorgungsleistungen": {
}, "account_number": "6410"
"Netto-Pr\u00e4mie f. R\u00fcckdeckung k\u00fcnftiger Versorgungsleistungen": { },
"account_number": "6410" "Beitr\u00e4ge": {
}, "account_number": "6420"
"Beitr\u00e4ge": { },
"account_number": "6420" "Sonstige Abgaben": {
}, "account_number": "6430"
"Sonstige Abgaben": { },
"account_number": "6430" "Steuerlich abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": {
}, "account_number": "6436"
"Steuerlich abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": { },
"account_number": "6436" "Steuerlich nicht abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": {
}, "account_number": "6437"
"Steuerlich nicht abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": { },
"account_number": "6437" "Ausgleichsabgabe i. S. d. Schwerbehindertengesetzes": {
}, "account_number": "6440"
"Ausgleichsabgabe i. S. d. Schwerbehindertengesetzes": { },
"account_number": "6440" "Reparaturen und Instandhaltung von Bauten": {
}, "account_number": "6450"
"Reparaturen und Instandhaltung von Bauten": { },
"account_number": "6450" "Reparaturen und Instandhaltung von technischenAnlagen und Maschinen": {
}, "account_number": "6460"
"Reparaturen und Instandhaltung von technischenAnlagen und Maschinen": { },
"account_number": "6460" "Reparaturen und Instandhaltung von anderen Anlagen und Betriebs- und Gesch\u00e4ftsausstattung": {
}, "account_number": "6470"
"Reparaturen und Instandhaltung von anderen Anlagen und Betriebs- und Gesch\u00e4ftsausstattung": { },
"account_number": "6470" "Zuf\u00fchrung zu Aufwandsr\u00fcckstellungen": {
}, "account_number": "6475"
"Zuf\u00fchrung zu Aufwandsr\u00fcckstellungen": { },
"account_number": "6475" "Reparaturen und Instandhaltung von anderen Anlagen": {
}, "account_number": "6485"
"Reparaturen und Instandhaltung von anderen Anlagen": { },
"account_number": "6485" "Sonstige Reparaturen und Instandhaltungen": {
}, "account_number": "6490"
"Sonstige Reparaturen und Instandhaltungen": { },
"account_number": "6490" "Wartungskosten f. Hard- und Software": {
}, "account_number": "6495"
"Wartungskosten f. Hard- und Software": { },
"account_number": "6495" "Mietleasing (bewegliche Wirtschaftsg\u00fcter)": {
}, "account_number": "6498"
"Mietleasing (bewegliche Wirtschaftsg\u00fcter)": {
"account_number": "6498"
}
}, },
"Fahrzeugkosten (Gruppe)": { "Fahrzeugkosten (Gruppe)": {
"is_group": 1, "is_group": 1,
"Fahrzeugkosten": { "Fahrzeugkosten": {
"account_number": "6500" "account_number": "6500"
}, },
"Kfz-Versicherungen (Gruppe)": { "Kfz-Versicherungen": {
"is_group": 1, "account_number": "6520"
"Kfz-Versicherungen": {
"account_number": "6520"
}
}, },
"Laufende Kfz-Betriebskosten (Gruppe)": { "Laufende Kfz-Betriebskosten": {
"is_group": 1, "account_number": "6530"
"Laufende Kfz-Betriebskosten": {
"account_number": "6530"
}
}, },
"Kfz-Reparaturen (Gruppe)": { "Kfz-Reparaturen": {
"is_group": 1, "account_number": "6540"
"Kfz-Reparaturen": {
"account_number": "6540"
}
}, },
"Garagenmiete (Gruppe)": { "Garagenmiete": {
"is_group": 1, "account_number": "6550"
"Garagenmiete": {
"account_number": "6550"
}
}, },
"Mietleasing Kfz (Gruppe)": { "Mietleasing Kfz": {
"is_group": 1, "account_number": "6560"
"Mietleasing Kfz": {
"account_number": "6560"
}
}, },
"Sonstige Kfz-Kosten (Gruppe)": { "Sonstige Kfz-Kosten": {
"is_group": 1, "account_number": "6570"
"Sonstige Kfz-Kosten": {
"account_number": "6570"
}
}, },
"Mautgeb\u00fchren (Gruppe)": { "Mautgeb\u00fchren": {
"is_group": 1, "account_number": "6580"
"Mautgeb\u00fchren": {
"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"
@@ -3006,20 +2970,23 @@
"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": { "Reisekosten Arbeitnehmer (Gruppe)": {
"account_number": "6650" "is_group": 1,
}, "Reisekosten Arbeitnehmer": {
"Reisekosten Arbeitnehmer \u00dcbernachtungsaufwand": { "account_number": "6650"
"account_number": "6660" },
}, "Reisekosten Arbeitnehmer \u00dcbernachtungsaufwand": {
"Reisekosten Arbeitnehmer Fahrtkosten": { "account_number": "6660"
"account_number": "6663" },
}, "Reisekosten Arbeitnehmer Fahrtkosten": {
"Reisekosten Arbeitnehmer Verpflegungsmehraufwand": { "account_number": "6663"
"account_number": "6664" },
}, "Reisekosten Arbeitnehmer Verpflegungsmehraufwand": {
"Kilometergelderstattung Arbeitnehmer": { "account_number": "6664"
"account_number": "6668" },
"Kilometergelderstattung Arbeitnehmer": {
"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

@@ -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

@@ -108,7 +108,7 @@ def build_forest(data):
error_messages = [] error_messages = []
for i in data: for i in data:
account_name, _, account_number, is_group, account_type, root_type = i account_name, __, account_number, is_group, account_type, root_type = i
if not account_name: if not account_name:
error_messages.append("Row {0}: Please enter Account Name".format(line_no)) error_messages.append("Row {0}: Please enter Account Name".format(line_no))

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 18:26:01.540170",
"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

@@ -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

@@ -456,8 +456,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
@@ -469,9 +471,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

@@ -126,12 +126,12 @@ class PaymentRequest(Document):
return controller.get_payment_url(**{ return controller.get_payment_url(**{
"amount": flt(self.grand_total, self.precision("grand_total")), "amount": flt(self.grand_total, self.precision("grand_total")),
"title": data.company.encode("utf-8"), "title": frappe.as_unicode(data.company),
"description": self.subject.encode("utf-8"), "description": frappe.as_unicode(self.subject),
"reference_doctype": "Payment Request", "reference_doctype": "Payment Request",
"reference_docname": self.name, "reference_docname": self.name,
"payer_email": self.email_to or frappe.session.user, "payer_email": self.email_to or frappe.session.user,
"payer_name": frappe.safe_encode(data.customer_name), "payer_name": frappe.as_unicode(data.customer_name),
"order_id": self.name, "order_id": self.name,
"currency": self.currency "currency": self.currency
}) })
@@ -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
}) })
@@ -475,4 +475,4 @@ def make_payment_order(source_name, target_doc=None):
} }
}, target_doc, set_missing_values) }, target_doc, set_missing_values)
return doclist return doclist

View File

@@ -146,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

View File

@@ -760,7 +760,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",
"in_preview": 1, "in_preview": 1,
"label": "Asset Category", "label": "Asset Category",
"options": "Asset Category", "options": "Asset Category",
@@ -770,7 +770,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

@@ -429,13 +429,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"))
@@ -466,13 +470,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

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

@@ -4,126 +4,137 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import formatdate, getdate, flt, add_days from frappe.utils import formatdate, flt, add_days
def execute(filters=None): def execute(filters=None):
filters.day_before_from_date = add_days(filters.from_date, -1) filters.day_before_from_date = add_days(filters.from_date, -1)
columns, data = get_columns(filters), get_data(filters) columns, data = get_columns(filters), get_data(filters)
return columns, data return columns, data
def get_data(filters): def get_data(filters):
data = [] data = []
asset_categories = get_asset_categories(filters) asset_categories = get_asset_categories(filters)
assets = get_assets(filters) assets = get_assets(filters)
asset_costs = get_asset_costs(assets, filters)
asset_depreciations = get_accumulated_depreciations(assets, filters)
for asset_category in asset_categories: for asset_category in asset_categories:
row = frappe._dict() row = frappe._dict()
row.asset_category = asset_category # row.asset_category = asset_category
row.update(asset_costs.get(asset_category)) row.update(asset_category)
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) -
flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
row.update(next(asset for asset in assets if asset["asset_category"] == asset_category.get("asset_category", "")))
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated_during_the_period))
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
flt(row.accumulated_depreciation_as_on_from_date))
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
flt(row.accumulated_depreciation_as_on_to_date))
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase)
- flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
row.update(asset_depreciations.get(asset_category))
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
flt(row.accumulated_depreciation_as_on_from_date))
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
flt(row.accumulated_depreciation_as_on_to_date))
data.append(row) data.append(row)
return data return data
def get_asset_categories(filters): def get_asset_categories(filters):
return frappe.db.sql_list(""" return frappe.db.sql("""
select distinct asset_category from `tabAsset` SELECT asset_category,
where docstatus=1 and company=%s and purchase_date <= %s ifnull(sum(case when purchase_date < %(from_date)s then
""", (filters.company, filters.to_date)) case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then
gross_purchase_amount
else
0
end
else
0
end), 0) as cost_as_on_from_date,
ifnull(sum(case when purchase_date >= %(from_date)s then
gross_purchase_amount
else
0
end), 0) as cost_of_new_purchase,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Sold" then
gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_sold_asset,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Scrapped" then
gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_scrapped_asset
from `tabAsset`
where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s
group by asset_category
""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
def get_assets(filters): def get_assets(filters):
return frappe.db.sql(""" return frappe.db.sql("""
select name, asset_category, purchase_date, gross_purchase_amount, disposal_date, status SELECT results.asset_category,
from `tabAsset` sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
where docstatus=1 and company=%s and purchase_date <= %s""", sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
(filters.company, filters.to_date), as_dict=1) sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category,
def get_asset_costs(assets, filters): ifnull(sum(case when ds.schedule_date < %(from_date)s then
asset_costs = frappe._dict() ds.depreciation_amount
for d in assets: else
asset_costs.setdefault(d.asset_category, frappe._dict({ 0
"cost_as_on_from_date": 0, end), 0) as accumulated_depreciation_as_on_from_date,
"cost_of_new_purchase": 0, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
"cost_of_sold_asset": 0, and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then
"cost_of_scrapped_asset": 0 ds.depreciation_amount
})) else
0
costs = asset_costs[d.asset_category] end), 0) as depreciation_eliminated_during_the_period,
ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
if getdate(d.purchase_date) < getdate(filters.from_date): and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
if not d.disposal_date or getdate(d.disposal_date) >= getdate(filters.from_date): ds.depreciation_amount
costs.cost_as_on_from_date += flt(d.gross_purchase_amount) else
else: 0
costs.cost_of_new_purchase += flt(d.gross_purchase_amount) end), 0) as depreciation_amount_during_the_period
from `tabAsset` a, `tabDepreciation Schedule` ds
if d.disposal_date and getdate(d.disposal_date) >= getdate(filters.from_date) \ where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent
and getdate(d.disposal_date) <= getdate(filters.to_date): group by a.asset_category
if d.status == "Sold": union
costs.cost_of_sold_asset += flt(d.gross_purchase_amount) SELECT a.asset_category,
elif d.status == "Scrapped": ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
costs.cost_of_scrapped_asset += flt(d.gross_purchase_amount) and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s)
then
return asset_costs 0
else
def get_accumulated_depreciations(assets, filters): a.opening_accumulated_depreciation
asset_depreciations = frappe._dict() end), 0) as accumulated_depreciation_as_on_from_date,
for d in assets: ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
asset = frappe.get_doc("Asset", d.name) a.opening_accumulated_depreciation
else
if d.asset_category in asset_depreciations: 0
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] += asset.opening_accumulated_depreciation end), 0) as depreciation_eliminated_during_the_period,
else: 0 as depreciation_amount_during_the_period
asset_depreciations.setdefault(d.asset_category, frappe._dict({ from `tabAsset` a
"accumulated_depreciation_as_on_from_date": asset.opening_accumulated_depreciation, where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s
"depreciation_amount_during_the_period": 0, group by a.asset_category) as results
"depreciation_eliminated_during_the_period": 0 group by results.asset_category
})) """, {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
depr = asset_depreciations[d.asset_category]
if not asset.schedules: # if no schedule,
if asset.disposal_date:
# and disposal is NOT within the period, then opening accumulated depreciation not included
if getdate(asset.disposal_date) < getdate(filters.from_date) or getdate(asset.disposal_date) > getdate(filters.to_date):
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] = 0
# if no schedule, and disposal is within period, accumulated dep is the amount eliminated
if getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
depr.depreciation_eliminated_during_the_period += asset.opening_accumulated_depreciation
for schedule in asset.get("schedules"):
if getdate(schedule.schedule_date) < getdate(filters.from_date):
if not asset.disposal_date or getdate(asset.disposal_date) >= getdate(filters.from_date):
depr.accumulated_depreciation_as_on_from_date += flt(schedule.depreciation_amount)
elif getdate(schedule.schedule_date) <= getdate(filters.to_date):
if not asset.disposal_date:
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
else:
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
if asset.disposal_date and getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
depr.depreciation_eliminated_during_the_period += flt(schedule.depreciation_amount)
return asset_depreciations
def get_columns(filters): def get_columns(filters):
return [ return [
{ {

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'")
@@ -412,11 +412,14 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)")
else: else:
additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)")
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-01-28 13:52:22.513425", "modified": "2020-03-18 18:25:59.283057",
"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

@@ -9,7 +9,8 @@ def get_data():
'heatmap_message': _('This is based on transactions against this Supplier. See timeline below for details'), 'heatmap_message': _('This is based on transactions against this Supplier. See timeline below for details'),
'fieldname': 'supplier', 'fieldname': 'supplier',
'non_standard_fieldnames': { 'non_standard_fieldnames': {
'Payment Entry': 'party_name' 'Payment Entry': 'party_name',
'Bank Account': 'party'
}, },
'transactions': [ 'transactions': [
{ {
@@ -24,6 +25,10 @@ def get_data():
'label': _('Payments'), 'label': _('Payments'),
'items': ['Payment Entry'] 'items': ['Payment Entry']
}, },
{
'label': _('Bank'),
'items': ['Bank Account']
},
{ {
'label': _('Pricing'), 'label': _('Pricing'),
'items': ['Pricing Rule'] 'items': ['Pricing Rule']

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

@@ -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

@@ -44,6 +44,7 @@ class BuyingController(StockController):
self.validate_stock_or_nonstock_items() self.validate_stock_or_nonstock_items()
self.validate_warehouse() self.validate_warehouse()
self.set_supplier_address() self.set_supplier_address()
self.validate_asset_return()
if self.doctype=="Purchase Invoice": if self.doctype=="Purchase Invoice":
self.validate_purchase_receipt_if_update_stock() self.validate_purchase_receipt_if_update_stock()
@@ -99,6 +100,19 @@ class BuyingController(StockController):
for d in tax_for_valuation: for d in tax_for_valuation:
d.category = 'Total' d.category = 'Total'
msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items')) msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items'))
def validate_asset_return(self):
if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return:
return
purchase_doc_field = 'purchase_receipt' if self.doctype == 'Purchase Receipt' else 'purchase_invoice'
not_cancelled_asset = [d.name for d in frappe.db.get_all("Asset", {
purchase_doc_field: self.return_against,
"docstatus": 1
})]
if self.is_return and len(not_cancelled_asset):
frappe.throw(_("{} has submitted assets linked to it. You need to cancel the assets to create purchase return.".format(self.return_against)),
title=_("Not Allowed"))
def get_asset_items(self): def get_asset_items(self):
if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']:
@@ -644,19 +658,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:
assets_link = list(map(lambda d: frappe.utils.get_link_to_form('Asset', d), created_assets))
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:
frappe.throw(_("Row {1}: Asset Naming Series is mandatory for the auto creation for item {0}") frappe.throw(_("Row {}: Asset Naming Series is mandatory for the auto creation for item {}")
.format(d.item_code, d.idx)) .format(d.idx, frappe.bold(d.item_code)))
else: else:
messages.append(_("Assets not created for <b>{0}</b>. You will have to create asset manually.") messages.append(_("Assets not created for {0}. You will have to create asset manually.")
.format(d.item_code)) .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:
@@ -688,6 +715,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:
@@ -717,7 +746,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
@@ -998,4 +1027,4 @@ def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty
available_batches.append({'batch': batch, 'qty': available_qty}) available_batches.append({'batch': batch, 'qty': available_qty})
required_qty -= available_qty required_qty -= available_qty
return available_batches return available_batches

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

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
@@ -208,7 +209,8 @@
{ {
"fieldname": "opportunity_amount", "fieldname": "opportunity_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Opportunity Amount" "label": "Opportunity Amount",
"options": "currency"
}, },
{ {
"default": "0", "default": "0",
@@ -412,7 +414,8 @@
], ],
"icon": "fa fa-info-sign", "icon": "fa fa-info-sign",
"idx": 195, "idx": 195,
"modified": "2019-09-30 12:58:37.385400", "links": [],
"modified": "2020-03-20 12:28:45.228994",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Opportunity", "name": "Opportunity",

View File

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

@@ -5,20 +5,16 @@ frappe.ui.form.on('Clinical Procedure', {
setup: function(frm) { setup: function(frm) {
frm.set_query('batch_no', 'items', function(doc, cdt, cdn) { frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
var item = locals[cdt][cdn]; var item = locals[cdt][cdn];
if(!item.item_code) { if (!item.item_code) {
frappe.throw(__("Please enter Item Code to get Batch Number")); frappe.throw(__('Please enter Item Code to get Batch Number'));
} else { } else {
let filters = {'item_code': item.item_code};
if (frm.doc.status == 'In Progress') { if (frm.doc.status == 'In Progress') {
var filters = { filters['posting_date'] = frm.doc.start_date || frappe.datetime.nowdate();
'item_code': item.item_code, if (frm.doc.warehouse) filters['warehouse'] = frm.doc.warehouse;
'posting_date': frm.doc.start_date || frappe.datetime.nowdate()
};
if(frm.doc.warehouse) filters["warehouse"] = frm.doc.warehouse;
} else {
filters = {
'item_code': item.item_code
};
} }
return { return {
query : "erpnext.controllers.queries.get_batch_no", query : "erpnext.controllers.queries.get_batch_no",
filters: filters filters: filters
@@ -29,7 +25,7 @@ frappe.ui.form.on('Clinical Procedure', {
refresh: function(frm) { refresh: function(frm) {
frm.set_query("patient", function () { frm.set_query("patient", function () {
return { return {
filters: {"disabled": 0} filters: {"status": "Active"}
}; };
}); });
frm.set_query("appointment", function () { frm.set_query("appointment", function () {

View File

@@ -31,17 +31,7 @@ frappe.ui.form.on('Clinical Procedure Template', {
if(!frm.doc.__islocal) { if(!frm.doc.__islocal) {
cur_frm.add_custom_button(__('Change Item Code'), function() { cur_frm.add_custom_button(__('Change Item Code'), function() {
change_template_code(frm.doc); change_template_code(frm.doc);
} ); });
if(frm.doc.disabled == 1){
cur_frm.add_custom_button(__('Enable Template'), function() {
enable_template(frm.doc);
} );
}
else{
cur_frm.add_custom_button(__('Disable Template'), function() {
disable_template(frm.doc);
} );
}
} }
} }
}); });
@@ -52,27 +42,6 @@ var mark_change_in_item = function(frm) {
} }
}; };
var disable_template = function(doc){
frappe.call({
method: "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.disable_enable_template",
args: {status: 1, name: doc.name, item_code: doc.item_code, is_billable: doc.is_billable},
callback: function(){
cur_frm.reload_doc();
}
});
};
var enable_template = function(doc){
frappe.call({
method: "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.disable_enable_template",
args: {status: 0, name: doc.name, item_code: doc.item_code, is_billable: doc.is_billable},
callback: function(){
cur_frm.reload_doc();
}
});
};
var change_template_code = function(doc){ var change_template_code = function(doc){
var d = new frappe.ui.Dialog({ var d = new frappe.ui.Dialog({
title:__("Change Template Code"), title:__("Change Template Code"),

View File

@@ -9,26 +9,36 @@ from frappe.model.document import Document
from frappe.utils import nowdate from frappe.utils import nowdate
class ClinicalProcedureTemplate(Document): class ClinicalProcedureTemplate(Document):
def on_update(self): def validate(self):
#Item and Price List update --> if (change_in_item) self.enable_disable_item()
if(self.change_in_item and self.is_billable == 1 and self.item):
updating_item(self)
if(self.rate != 0.0):
updating_rate(self)
elif(self.is_billable == 0 and self.item):
frappe.db.set_value("Item",self.item,"disabled",1)
frappe.db.set_value(self.doctype,self.name,"change_in_item",0)
self.reload()
def after_insert(self): def after_insert(self):
create_item_from_template(self) create_item_from_template(self)
def on_update(self):
#Item and Price List update --> if (change_in_item)
if self.change_in_item and self.is_billable == 1 and self.item:
updating_item(self)
if self.rate != 0.0:
updating_rate(self)
elif self.is_billable == 0 and self.item:
frappe.db.set_value('Item',self.item,'disabled',1)
frappe.db.set_value(self.doctype,self.name,'change_in_item',0)
self.reload()
def enable_disable_item(self):
if self.is_billable:
if self.disabled:
frappe.db.set_value('Item', self.item, 'disabled', 1)
else:
frappe.db.set_value('Item', self.item, 'disabled', 0)
#Call before delete the template #Call before delete the template
def on_trash(self): def on_trash(self):
if(self.item): if(self.item):
try: try:
frappe.delete_doc("Item",self.item) frappe.delete_doc('Item',self.item)
except Exception: except Exception:
frappe.throw(_("""Not permitted. Please disable the Procedure Template""")) frappe.throw(_("""Not permitted. Please disable the Procedure Template"""))
@@ -40,7 +50,7 @@ class ClinicalProcedureTemplate(Document):
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""", and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""",
(args.get('item_code'), nowdate()), as_dict = 1) (args.get('item_code'), nowdate()), as_dict = 1)
if not item: if not item:
frappe.throw(_("Item {0} is not active or end of life has been reached").format(args.get('item_code'))) frappe.throw(_('Item {0} is not active or end of life has been reached').format(args.get('item_code')))
item = item[0] item = item[0]
@@ -63,46 +73,47 @@ def updating_rate(self):
item_code=%s""",(self.template, self.rate, self.item)) item_code=%s""",(self.template, self.rate, self.item))
def create_item_from_template(doc): def create_item_from_template(doc):
if(doc.is_billable == 1): disabled = 1
if doc.is_billable:
disabled = 0 disabled = 0
else:
disabled = 1
#insert item #insert item
item = frappe.get_doc({ item = frappe.get_doc({
"doctype": "Item", 'doctype': 'Item',
"item_code": doc.template, 'item_code': doc.template,
"item_name":doc.template, 'item_name':doc.template,
"item_group": doc.item_group, 'item_group': doc.item_group,
"description":doc.description, 'description':doc.description,
"is_sales_item": 1, 'is_sales_item': 1,
"is_service_item": 1, 'is_service_item': 1,
"is_purchase_item": 0, 'is_purchase_item': 0,
"is_stock_item": 0, 'is_stock_item': 0,
"show_in_website": 0, 'show_in_website': 0,
"is_pro_applicable": 0, 'is_pro_applicable': 0,
"disabled": disabled, 'disabled': disabled,
"stock_uom": "Unit" 'stock_uom': 'Unit'
}).insert(ignore_permissions=True) }).insert(ignore_permissions=True)
#insert item price #insert item price
#get item price list to insert item price #get item price list to insert item price
if(doc.rate != 0.0): if(doc.rate != 0.0):
price_list_name = frappe.db.get_value("Price List", {"selling": 1}) price_list_name = frappe.db.get_value('Price List', {'selling': 1})
if(doc.rate): if(doc.rate):
make_item_price(item.name, price_list_name, doc.rate) make_item_price(item.name, price_list_name, doc.rate)
else: else:
make_item_price(item.name, price_list_name, 0.0) make_item_price(item.name, price_list_name, 0.0)
#Set item to the template #Set item to the template
frappe.db.set_value("Clinical Procedure Template", doc.name, "item", item.name) frappe.db.set_value('Clinical Procedure Template', doc.name, 'item', item.name)
doc.reload() #refresh the doc after insert. doc.reload() #refresh the doc after insert.
def make_item_price(item, price_list_name, item_price): def make_item_price(item, price_list_name, item_price):
frappe.get_doc({ frappe.get_doc({
"doctype": "Item Price", 'doctype': 'Item Price',
"price_list": price_list_name, 'price_list': price_list_name,
"item_code": item, 'item_code': item,
"price_list_rate": item_price 'price_list_rate': item_price
}).insert(ignore_permissions=True) }).insert(ignore_permissions=True)
@frappe.whitelist() @frappe.whitelist()
@@ -111,20 +122,11 @@ def change_item_code_from_template(item_code, doc):
doc = frappe._dict(args) doc = frappe._dict(args)
if(frappe.db.exists({ if(frappe.db.exists({
"doctype": "Item", 'doctype': 'Item',
"item_code": item_code})): 'item_code': item_code})):
frappe.throw(_("Code {0} already exist").format(item_code)) frappe.throw(_('Code {0} already exist').format(item_code))
else: else:
frappe.rename_doc("Item", doc.item_code, item_code, ignore_permissions = True) frappe.rename_doc('Item', doc.item_code, item_code, ignore_permissions=True)
frappe.db.set_value("Clinical Procedure Template", doc.name, "item_code", item_code) frappe.db.set_value('Clinical Procedure Template', doc.name, 'item_code', item_code)
return return
@frappe.whitelist()
def disable_enable_template(status, name, item_code):
frappe.db.set_value("Clinical Procedure Template", name, "disabled", status)
if (frappe.db.exists({ #set Item's disabled field to status
"doctype": "Item",
"item_code": item_code})):
frappe.db.set_value("Item", item_code, "disabled", status)
return

View File

@@ -1,542 +1,206 @@
{ {
"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,
"engine": "InnoDB",
"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:26:06.360941",
"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

@@ -78,6 +78,7 @@
}, },
{ {
"default": "0", "default": "0",
"description": "Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.",
"fieldname": "collect_registration_fee", "fieldname": "collect_registration_fee",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Collect Fee for Patient Registration" "label": "Collect Fee for Patient Registration"

View File

@@ -290,19 +290,23 @@ def insert_lab_test_to_medical_record(doc):
comment = "" comment = ""
if item.lab_test_comment: if item.lab_test_comment:
comment = str(item.lab_test_comment) comment = str(item.lab_test_comment)
event = "" table_row = item.lab_test_name
if item.lab_test_event: if item.lab_test_event:
event = item.lab_test_event table_row += " " + item.lab_test_event
table_row = item.lab_test_name +" "+ event +" "+ item.result_value
if item.result_value:
table_row += " " + item.result_value
if item.normal_range: if item.normal_range:
table_row += " normal_range("+item.normal_range+")" table_row += " normal_range("+item.normal_range+")"
table_row += " "+comment table_row += " " + comment
elif doc.special_test_items: elif doc.special_test_items:
item = doc.special_test_items[0] item = doc.special_test_items[0]
if item.lab_test_particulars and item.result_value: if item.lab_test_particulars and item.result_value:
table_row = item.lab_test_particulars +" "+ item.result_value table_row = item.lab_test_particulars + " " + item.result_value
elif doc.sensitivity_test_items: elif doc.sensitivity_test_items:
item = doc.sensitivity_test_items[0] item = doc.sensitivity_test_items[0]

View File

@@ -5,6 +5,7 @@
from __future__ import unicode_literals 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.model.rename_doc import rename_doc
from frappe import _ from frappe import _
class LabTestTemplate(Document): class LabTestTemplate(Document):
@@ -98,13 +99,11 @@ def create_item_from_template(doc):
# get item price list to insert item price # get item price list to insert item price
if doc.lab_test_rate != 0.0: if doc.lab_test_rate != 0.0:
price_list_name = frappe.db.get_value("Price List", {"selling": 1}) price_list_name = frappe.db.get_value("Price List", {"selling": 1})
if(doc.lab_test_rate): if doc.lab_test_rate:
make_item_price(item.name, price_list_name, doc.lab_test_rate) make_item_price(item.name, price_list_name, doc.lab_test_rate)
item.standard_rate = doc.lab_test_rate
else: else:
make_item_price(item.name, price_list_name, 0.0) make_item_price(item.name, price_list_name, 0.0)
item.standard_rate = 0.0
item.save(ignore_permissions = True)
# Set item in the template # Set item in the template
frappe.db.set_value("Lab Test Template", doc.name, "item", item.name) frappe.db.set_value("Lab Test Template", doc.name, "item", item.name)

View File

@@ -15,7 +15,7 @@ frappe.ui.form.on('Patient', {
} else { } else {
erpnext.toggle_naming_series(); erpnext.toggle_naming_series();
} }
if (frappe.defaults.get_default("collect_registration_fee") && frm.doc.disabled == 1) { if (frappe.defaults.get_default("collect_registration_fee") && frm.doc.status == 'Disabled') {
frm.add_custom_button(__('Invoice Patient Registration'), function () { frm.add_custom_button(__('Invoice Patient Registration'), function () {
btn_invoice_registration(frm); btn_invoice_registration(frm);
}); });

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_copy": 1, "allow_copy": 1,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
@@ -19,15 +20,14 @@
"blood_group", "blood_group",
"dob", "dob",
"age_html", "age_html",
"status",
"image", "image",
"column_break_14", "column_break_14",
"status",
"customer", "customer",
"report_preference", "report_preference",
"mobile", "mobile",
"email", "email",
"phone", "phone",
"disabled",
"sb_relation", "sb_relation",
"patient_relation", "patient_relation",
"allergy_medical_and_surgical_history", "allergy_medical_and_surgical_history",
@@ -125,15 +125,15 @@
"report_hide": 1 "report_hide": 1
}, },
{ {
"default": "Active",
"fieldname": "status", "fieldname": "status",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 1, "in_filter": 1,
"in_list_view": 1,
"label": "Status", "label": "Status",
"no_copy": 1, "no_copy": 1,
"options": "Active\nDormant\nOpen", "options": "Active\nDisabled",
"print_hide": 1, "print_hide": 1,
"report_hide": 1 "read_only": 1
}, },
{ {
"fieldname": "image", "fieldname": "image",
@@ -183,19 +183,8 @@
"fieldname": "phone", "fieldname": "phone",
"fieldtype": "Data", "fieldtype": "Data",
"in_filter": 1, "in_filter": 1,
"in_list_view": 1,
"label": "Phone" "label": "Phone"
}, },
{
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 1,
"label": "Disabled",
"no_copy": 1,
"print_hide": 1,
"report_hide": 1
},
{ {
"collapsible": 1, "collapsible": 1,
"fieldname": "sb_relation", "fieldname": "sb_relation",
@@ -346,8 +335,9 @@
], ],
"icon": "fa fa-user", "icon": "fa fa-user",
"image_field": "image", "image_field": "image",
"links": [],
"max_attachments": 50, "max_attachments": 50,
"modified": "2019-09-25 23:30:49.905893", "modified": "2020-01-29 11:22:40.698125",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Patient", "name": "Patient",

View File

@@ -15,8 +15,8 @@ class Patient(Document):
def after_insert(self): def after_insert(self):
if(frappe.db.get_value("Healthcare Settings", None, "manage_customer") == '1' and not self.customer): if(frappe.db.get_value("Healthcare Settings", None, "manage_customer") == '1' and not self.customer):
create_customer(self) create_customer(self)
if(frappe.db.get_value("Healthcare Settings", None, "collect_registration_fee") == '1'): if frappe.db.get_single_value('Healthcare Settings', 'collect_registration_fee'):
frappe.db.set_value("Patient", self.name, "disabled", 1) frappe.db.set_value('Patient', self.name, 'status', 'Disabled')
else: else:
send_registration_sms(self) send_registration_sms(self)
self.reload() self.reload()
@@ -62,7 +62,7 @@ class Patient(Document):
return age_str return age_str
def invoice_patient_registration(self): def invoice_patient_registration(self):
frappe.db.set_value("Patient", self.name, "disabled", 0) frappe.db.set_value("Patient", self.name, "status", "Active")
send_registration_sms(self) send_registration_sms(self)
if(flt(frappe.get_value("Healthcare Settings", None, "registration_fee"))>0): if(flt(frappe.get_value("Healthcare Settings", None, "registration_fee"))>0):
company = frappe.defaults.get_user_default('company') company = frappe.defaults.get_user_default('company')

View File

@@ -11,7 +11,7 @@ frappe.ui.form.on('Patient Appointment', {
refresh: function(frm) { refresh: function(frm) {
frm.set_query("patient", function () { frm.set_query("patient", function () {
return { return {
filters: {"disabled": 0} filters: {"status": "Active"}
}; };
}); });
frm.set_query("practitioner", function() { frm.set_query("practitioner", function() {
@@ -288,12 +288,19 @@ var check_and_set_availability = function(frm) {
}; };
var get_procedure_prescribed = function(frm){ var get_procedure_prescribed = function(frm){
if(frm.doc.patient){ if (frm.doc.patient) {
frappe.call({ frappe.call({
method:"erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_procedure_prescribed", method:"erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_procedure_prescribed",
args: {patient: frm.doc.patient}, args: {patient: frm.doc.patient},
callback: function(r){ callback: function(r) {
show_procedure_templates(frm, r.message); if (r.message && r.message.length) {
show_procedure_templates(frm, r.message);
} else {
frappe.msgprint({
title: __('Not Found'),
message: __('No Prescribed Procedures found for the selected Patient')
});
}
} }
}); });
} }

View File

@@ -25,6 +25,7 @@ class PatientAppointment(Document):
self.reload() self.reload()
def validate(self): def validate(self):
self.set_appointment_datetime()
end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) + datetime.timedelta(minutes=float(self.duration)) end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) + datetime.timedelta(minutes=float(self.duration))
overlaps = frappe.db.sql(""" overlaps = frappe.db.sql("""
select select
@@ -44,6 +45,9 @@ class PatientAppointment(Document):
frappe.throw(_("""Appointment overlaps with {0}.<br> {1} has appointment scheduled frappe.throw(_("""Appointment overlaps with {0}.<br> {1} has appointment scheduled
with {2} at {3} having {4} minute(s) duration.""").format(overlaps[0][0], overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4])) with {2} at {3} having {4} minute(s) duration.""").format(overlaps[0][0], overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4]))
def set_appointment_datetime(self):
self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00")
def after_insert(self): def after_insert(self):
if self.procedure_prescription: if self.procedure_prescription:
frappe.db.set_value("Procedure Prescription", self.procedure_prescription, "appointment_booked", True) frappe.db.set_value("Procedure Prescription", self.procedure_prescription, "appointment_booked", True)
@@ -319,27 +323,29 @@ def create_encounter(appointment):
return encounter.as_dict() return encounter.as_dict()
def remind_appointment(): def set_appointment_reminder():
if frappe.db.get_value("Healthcare Settings", None, "app_rem") == '1': if frappe.db.get_single_value("Healthcare Settings", "app_rem"):
rem_before = datetime.datetime.strptime(frappe.get_value("Healthcare Settings", None, "rem_before"), "%H:%M:%S") remind_before = datetime.datetime.strptime(frappe.db.get_single_value("Healthcare Settings", "rem_before"), '%H:%M:%S')
rem_dt = datetime.datetime.now() + datetime.timedelta(
hours=rem_before.hour, minutes=rem_before.minute, seconds=rem_before.second)
appointment_list = frappe.db.sql( reminder_dt = datetime.datetime.now() + datetime.timedelta(
"select name from `tabPatient Appointment` where start_dt between %s and %s and reminded = 0 ", hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second)
(datetime.datetime.now(), rem_dt)
)
for i in range(0, len(appointment_list)): appointment_list = frappe.db.get_all("Patient Appointment", {
doc = frappe.get_doc("Patient Appointment", appointment_list[i][0]) "appointment_datetime": ["between", (datetime.datetime.now(), reminder_dt)],
message = frappe.db.get_value("Healthcare Settings", None, "app_rem_msg") "reminded": 0,
"status": ["!=", "Cancelled"]
})
for appointment in appointment_list:
doc = frappe.get_doc('Patient Appointment', appointment.name)
message = frappe.db.get_single_value("Healthcare Settings", "app_rem_msg")
send_message(doc, message) send_message(doc, message)
frappe.db.set_value("Patient Appointment", doc.name, "reminded",1) frappe.db.set_value('Patient Appointment', doc.name, 'reminded', 1)
def send_message(doc, message): def send_message(doc, message):
patient = frappe.get_doc("Patient", doc.patient) patient_mobile = frappe.db.get_value("Patient", doc.patient, "mobile")
if patient.mobile: if patient_mobile:
context = {"doc": doc, "alert": doc, "comments": None} context = {"doc": doc, "alert": doc, "comments": None}
if doc.get("_comments"): if doc.get("_comments"):
context["comments"] = json.loads(doc.get("_comments")) context["comments"] = json.loads(doc.get("_comments"))
@@ -386,5 +392,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

@@ -62,7 +62,7 @@ frappe.ui.form.on('Patient Encounter', {
frm.set_query("patient", function () { frm.set_query("patient", function () {
return { return {
filters: {"disabled": 0} filters: {"status": "Active"}
}; };
}); });
frm.set_query("drug_code", "drug_prescription", function() { frm.set_query("drug_code", "drug_prescription", function() {

View File

@@ -1,160 +1,71 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 0, "allow_import": 1,
"allow_import": 1, "allow_rename": 1,
"allow_rename": 1, "autoname": "field:schedule_name",
"autoname": "field:schedule_name", "beta": 1,
"beta": 1, "creation": "2017-05-03 17:28:03.926787",
"creation": "2017-05-03 17:28:03.926787", "doctype": "DocType",
"custom": 0, "editable_grid": 1,
"docstatus": 0, "engine": "InnoDB",
"doctype": "DocType", "field_order": [
"document_type": "", "disabled",
"editable_grid": 1, "schedule_details_section",
"engine": "InnoDB", "schedule_name",
"time_slots"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "fieldname": "schedule_name",
"allow_in_quick_entry": 0, "fieldtype": "Data",
"allow_on_submit": 0, "ignore_xss_filter": 1,
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Schedule Name",
"columns": 0, "reqd": 1,
"fieldname": "schedule_name", "unique": 1
"fieldtype": "Data", },
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Schedule 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,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "fieldname": "time_slots",
"allow_in_quick_entry": 0, "fieldtype": "Table",
"allow_on_submit": 0, "label": "Time Slots",
"bold": 0, "options": "Healthcare Schedule Time Slot"
"collapsible": 0, },
"columns": 0,
"fieldname": "time_slots",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Time Slots",
"length": 0,
"no_copy": 0,
"options": "Healthcare Schedule Time Slot",
"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, "default": "0",
"allow_in_quick_entry": 0, "fieldname": "disabled",
"allow_on_submit": 0, "fieldtype": "Check",
"bold": 0, "label": "Disabled",
"collapsible": 0, "print_hide": 1
"columns": 0, },
"fieldname": "disabled", {
"fieldtype": "Check", "fieldname": "schedule_details_section",
"hidden": 0, "fieldtype": "Section Break",
"ignore_user_permissions": 0, "label": "Schedule Details"
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disabled",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"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, "links": [],
"hide_heading": 0, "modified": "2020-01-31 12:21:45.975488",
"hide_toolbar": 0, "modified_by": "Administrator",
"idx": 0, "module": "Healthcare",
"image_view": 0, "name": "Practitioner Schedule",
"in_create": 0, "owner": "rmehta@gmail.com",
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-06-29 14:55:34.795995",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Practitioner Schedule",
"name_case": "",
"owner": "rmehta@gmail.com",
"permissions": [ "permissions": [
{ {
"amend": 0, "create": 1,
"cancel": 0, "delete": 1,
"create": 1, "email": 1,
"delete": 1, "export": 1,
"email": 1, "print": 1,
"export": 1, "read": 1,
"if_owner": 0, "report": 1,
"import": 0, "role": "Healthcare Administrator",
"permlevel": 0, "share": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0, "restrict_to_domain": "Healthcare",
"read_only": 0, "show_name_in_global_search": 1,
"read_only_onload": 0, "sort_field": "modified",
"restrict_to_domain": "Healthcare", "sort_order": "DESC",
"show_name_in_global_search": 1, "track_changes": 1
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
} }

View File

@@ -268,7 +268,9 @@ doc_events = {
scheduler_events = { scheduler_events = {
"all": [ "all": [
"erpnext.projects.doctype.project.project.project_status_update_reminder" "erpnext.projects.doctype.project.project.project_status_update_reminder",
"erpnext.healthcare_healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder"
], ],
"hourly": [ "hourly": [
'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails', 'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails',

View File

@@ -39,19 +39,21 @@ class AdditionalSalary(Document):
return amount_per_day * no_of_days return amount_per_day * no_of_days
@frappe.whitelist() @frappe.whitelist()
def get_additional_salary_component(employee, start_date, end_date): def get_additional_salary_component(employee, start_date, end_date, component_type):
additional_components = frappe.db.sql(""" additional_components = frappe.db.sql("""
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
from `tabAdditional Salary` from `tabAdditional Salary`
where employee=%(employee)s where employee=%(employee)s
and docstatus = 1 and docstatus = 1
and payroll_date between %(from_date)s and %(to_date)s and payroll_date between %(from_date)s and %(to_date)s
and type = %(component_type)s
group by salary_component, overwrite_salary_structure_amount group by salary_component, overwrite_salary_structure_amount
order by salary_component, overwrite_salary_structure_amount order by salary_component, overwrite_salary_structure_amount
""", { """, {
'employee': employee, 'employee': employee,
'from_date': start_date, 'from_date': start_date,
'to_date': end_date 'to_date': end_date,
'component_type': "Earning" if component_type == "earnings" else "Deduction"
}, as_dict=1) }, as_dict=1)
additional_components_list = [] additional_components_list = []

View File

@@ -1,569 +1,177 @@
{ {
"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": "",
"beta": 0,
"creation": "2013-02-05 11:48:26", "creation": "2013-02-05 11:48:26",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Setup", "document_type": "Setup",
"editable_grid": 0, "engine": "InnoDB",
"field_order": [
"department_name",
"parent_department",
"company",
"is_group",
"disabled",
"section_break_4",
"leave_block_list",
"leave_section",
"leave_approvers",
"expense_section",
"expense_approvers",
"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,
"fetch_if_empty": 0,
"fieldname": "department_name", "fieldname": "department_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": "Department", "label": "Department",
"length": 0,
"no_copy": 0,
"oldfieldname": "department_name", "oldfieldname": "department_name",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"permlevel": 0, "reqd": 1
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "parent_department", "fieldname": "parent_department",
"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 Department", "label": "Parent Department",
"length": 0, "options": "Department"
"no_copy": 0,
"options": "Department",
"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,
"fetch_if_empty": 0,
"fieldname": "company", "fieldname": "company",
"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": 1, "in_standard_filter": 1,
"label": "Company", "label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company", "options": "Company",
"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,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1, "bold": 1,
"collapsible": 0, "default": "0",
"columns": 0,
"fetch_if_empty": 0,
"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, "default": "0",
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "disabled", "fieldname": "disabled",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "label": "Disabled"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disabled",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 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,
"description": "Days for which Holidays are blocked for this department.", "description": "Days for which Holidays are blocked for this department.",
"fetch_if_empty": 0,
"fieldname": "leave_block_list", "fieldname": "leave_block_list",
"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": "Leave Block List", "label": "Leave Block List",
"length": 0, "options": "Leave Block List"
"no_copy": 0,
"options": "Leave Block List",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "leave_section", "fieldname": "leave_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Leave Approvers"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Leave Approvers",
"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,
"description": "The first Leave Approver in the list will be set as the default Leave Approver.", "description": "The first Leave Approver in the list will be set as the default Leave Approver.",
"fetch_if_empty": 0,
"fieldname": "leave_approvers", "fieldname": "leave_approvers",
"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": "Leave Approver", "label": "Leave Approver",
"length": 0, "options": "Department Approver"
"no_copy": 0,
"options": "Department Approver",
"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,
"fetch_if_empty": 0,
"fieldname": "expense_section", "fieldname": "expense_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Expense Approvers"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Expense Approvers",
"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,
"description": "The first Expense Approver in the list will be set as the default Expense Approver.", "description": "The first Expense Approver in the list will be set as the default Expense Approver.",
"fetch_if_empty": 0,
"fieldname": "expense_approvers", "fieldname": "expense_approvers",
"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": "Expense Approver", "label": "Expense Approver",
"length": 0, "options": "Department Approver"
"no_copy": 0,
"options": "Department Approver",
"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,
"fetch_if_empty": 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": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "read_only": 1
"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,
"fetch_if_empty": 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": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "read_only": 1
"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,
"fetch_if_empty": 0,
"fieldname": "old_parent", "fieldname": "old_parent",
"fieldtype": "Data", "fieldtype": "Data",
"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, "print_hide": 1
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-sitemap", "icon": "fa fa-sitemap",
"idx": 1, "idx": 1,
"image_view": 0, "is_tree": 1,
"in_create": 0, "links": [],
"is_submittable": 0, "modified": "2020-03-18 18:26:05.966022",
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-06-25 18:43:05.550387",
"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": [
{ {
"amend": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "HR User", "role": "HR User",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"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": "Academics User", "role": "Academics User",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 1, "import": 1,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "HR Manager", "role": "HR Manager",
"set_user_permissions": 1, "set_user_permissions": 1,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_order": "ASC", "sort_field": "modified",
"track_changes": 0, "sort_order": "ASC"
"track_seen": 0,
"track_views": 0
} }

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

@@ -122,7 +122,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)))
@@ -366,7 +366,8 @@ class LeaveApplication(Document):
leaves=self.total_leave_days * -1, leaves=self.total_leave_days * -1,
from_date=self.from_date, from_date=self.from_date,
to_date=self.to_date, to_date=self.to_date,
is_lwp=lwp is_lwp=lwp,
holiday_list=get_holiday_list_for_employee(self.employee)
) )
create_leave_ledger_entry(self, args, submit) create_leave_ledger_entry(self, args, submit)
@@ -376,7 +377,9 @@ class LeaveApplication(Document):
from_date=self.from_date, from_date=self.from_date,
to_date=expiry_date, to_date=expiry_date,
leaves=(date_diff(expiry_date, self.from_date) + 1) * -1, leaves=(date_diff(expiry_date, self.from_date) + 1) * -1,
is_lwp=lwp is_lwp=lwp,
holiday_list=get_holiday_list_for_employee(self.employee),
) )
create_leave_ledger_entry(self, args, submit) create_leave_ledger_entry(self, args, submit)
@@ -402,7 +405,7 @@ def get_allocation_expiry(employee, leave_type, to_date, from_date):
return expiry[0]['to_date'] if expiry else None return expiry[0]['to_date'] if expiry else None
@frappe.whitelist() @frappe.whitelist()
def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None): def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None, holiday_list = None):
number_of_days = 0 number_of_days = 0
if cint(half_day) == 1: if cint(half_day) == 1:
if from_date == to_date: if from_date == to_date:
@@ -416,7 +419,7 @@ def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day
number_of_days = date_diff(to_date, from_date) + 1 number_of_days = date_diff(to_date, from_date) + 1
if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"): if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"):
number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date)) number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date, holiday_list=holiday_list))
return number_of_days return number_of_days
@frappe.whitelist() @frappe.whitelist()
@@ -567,7 +570,7 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date):
{'name': leave_entry.transaction_name}, ['half_day_date']) {'name': leave_entry.transaction_name}, ['half_day_date'])
leave_days += get_number_of_leave_days(employee, leave_type, leave_days += get_number_of_leave_days(employee, leave_type,
leave_entry.from_date, leave_entry.to_date, half_day, half_day_date) * -1 leave_entry.from_date, leave_entry.to_date, half_day, half_day_date, holiday_list=leave_entry.holiday_list) * -1
return leave_days return leave_days
@@ -580,7 +583,7 @@ def get_leave_entries(employee, leave_type, from_date, to_date):
''' Returns leave entries between from_date and to_date ''' ''' Returns leave entries between from_date and to_date '''
return frappe.db.sql(""" return frappe.db.sql("""
SELECT SELECT
employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, holiday_list,
is_carry_forward, is_expired is_carry_forward, is_expired
FROM `tabLeave Ledger Entry` FROM `tabLeave Ledger Entry`
WHERE employee=%(employee)s AND leave_type=%(leave_type)s WHERE employee=%(employee)s AND leave_type=%(leave_type)s
@@ -596,9 +599,10 @@ def get_leave_entries(employee, leave_type, from_date, to_date):
}, as_dict=1) }, as_dict=1)
@frappe.whitelist() @frappe.whitelist()
def get_holidays(employee, from_date, to_date): def get_holidays(employee, from_date, to_date, holiday_list = None):
'''get holidays between two dates for the given employee''' '''get holidays between two dates for the given employee'''
holiday_list = get_holiday_list_for_employee(employee) if not holiday_list:
holiday_list = get_holiday_list_for_employee(employee)
holidays = frappe.db.sql("""select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2 holidays = frappe.db.sql("""select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2
where h1.parent = h2.name and h1.holiday_date between %s and %s where h1.parent = h2.name and h1.holiday_date between %s and %s

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"creation": "2019-05-09 15:47:39.760406", "creation": "2019-05-09 15:47:39.760406",
"doctype": "DocType", "doctype": "DocType",
"engine": "InnoDB", "engine": "InnoDB",
@@ -12,6 +13,7 @@
"column_break_7", "column_break_7",
"from_date", "from_date",
"to_date", "to_date",
"holiday_list",
"is_carry_forward", "is_carry_forward",
"is_expired", "is_expired",
"is_lwp", "is_lwp",
@@ -98,11 +100,18 @@
"fieldname": "is_lwp", "fieldname": "is_lwp",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Leave Without Pay" "label": "Is Leave Without Pay"
},
{
"fieldname": "holiday_list",
"fieldtype": "Link",
"label": "Holiday List",
"options": "Holiday List"
} }
], ],
"in_create": 1, "in_create": 1,
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-08-20 14:40:04.130799", "links": [],
"modified": "2020-02-27 14:40:10.502605",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Ledger Entry", "name": "Leave Ledger Entry",

View File

@@ -298,9 +298,11 @@ class SalarySlip(TransactionBase):
def calculate_net_pay(self): def calculate_net_pay(self):
if self.salary_structure: if self.salary_structure:
self.calculate_component_amounts() self.calculate_component_amounts("earnings")
self.gross_pay = self.get_component_totals("earnings") self.gross_pay = self.get_component_totals("earnings")
if self.salary_structure:
self.calculate_component_amounts("deductions")
self.total_deduction = self.get_component_totals("deductions") self.total_deduction = self.get_component_totals("deductions")
self.set_loan_repayment() self.set_loan_repayment()
@@ -308,25 +310,27 @@ class SalarySlip(TransactionBase):
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment)) self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
self.rounded_total = rounded(self.net_pay) self.rounded_total = rounded(self.net_pay)
def calculate_component_amounts(self): def calculate_component_amounts(self, component_type):
if not getattr(self, '_salary_structure_doc', None): if not getattr(self, '_salary_structure_doc', None):
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure) self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
self.add_structure_components() self.add_structure_components(component_type)
self.add_employee_benefits(payroll_period) self.add_additional_salary_components(component_type)
self.add_additional_salary_components() if component_type == "earnings":
self.add_tax_components(payroll_period) self.add_employee_benefits(payroll_period)
self.set_component_amounts_based_on_payment_days() else:
self.add_tax_components(payroll_period)
def add_structure_components(self): self.set_component_amounts_based_on_payment_days(component_type)
def add_structure_components(self, component_type):
data = self.get_data_for_eval() data = self.get_data_for_eval()
for key in ('earnings', 'deductions'): for struct_row in self._salary_structure_doc.get(component_type):
for struct_row in self._salary_structure_doc.get(key): amount = self.eval_condition_and_formula(struct_row, data)
amount = self.eval_condition_and_formula(struct_row, data) if amount and struct_row.statistical_component == 0:
if amount and struct_row.statistical_component == 0: self.update_component_row(struct_row, amount, component_type)
self.update_component_row(struct_row, amount, key)
def get_data_for_eval(self): def get_data_for_eval(self):
'''Returns data for evaluating formula''' '''Returns data for evaluating formula'''
@@ -399,14 +403,15 @@ class SalarySlip(TransactionBase):
amount = last_benefit.amount amount = last_benefit.amount
self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings") self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
def add_additional_salary_components(self): def add_additional_salary_components(self, component_type):
additional_components = get_additional_salary_component(self.employee, self.start_date, self.end_date) additional_components = get_additional_salary_component(self.employee,
self.start_date, self.end_date, component_type)
if additional_components: if additional_components:
for additional_component in additional_components: for additional_component in additional_components:
amount = additional_component.amount amount = additional_component.amount
overwrite = additional_component.overwrite overwrite = additional_component.overwrite
key = "earnings" if additional_component.type == "Earning" else "deductions" self.update_component_row(frappe._dict(additional_component.struct_row), amount,
self.update_component_row(frappe._dict(additional_component.struct_row), amount, key, overwrite=overwrite) component_type, overwrite=overwrite)
def add_tax_components(self, payroll_period): def add_tax_components(self, payroll_period):
# Calculate variable_based_on_taxable_salary after all components updated in salary slip # Calculate variable_based_on_taxable_salary after all components updated in salary slip
@@ -735,7 +740,7 @@ class SalarySlip(TransactionBase):
total += d.amount total += d.amount
return total return total
def set_component_amounts_based_on_payment_days(self): def set_component_amounts_based_on_payment_days(self, component_type):
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee, joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
["date_of_joining", "relieving_date"]) ["date_of_joining", "relieving_date"])
@@ -745,9 +750,8 @@ class SalarySlip(TransactionBase):
if not joining_date: if not joining_date:
frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name))) frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
for component_type in ("earnings", "deductions"): for d in self.get(component_type):
for d in self.get(component_type): d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
def set_loan_repayment(self): def set_loan_repayment(self):
self.set('loans', []) self.set('loans', [])

View File

@@ -25,7 +25,6 @@ class TestSalaryStructure(unittest.TestCase):
make_employee("test_employee@salary.com") make_employee("test_employee@salary.com")
make_employee("test_employee_2@salary.com") make_employee("test_employee_2@salary.com")
def make_holiday_list(self): def make_holiday_list(self):
if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"): if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
holiday_list = frappe.get_doc({ holiday_list = frappe.get_doc({
@@ -38,6 +37,29 @@ class TestSalaryStructure(unittest.TestCase):
holiday_list.get_weekly_off_dates() holiday_list.get_weekly_off_dates()
holiday_list.save() holiday_list.save()
def test_salary_structure_deduction_based_on_gross_pay(self):
emp = make_employee("test_employee_3@salary.com")
sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit = True)
sal_struct.earnings = [sal_struct.earnings[0]]
sal_struct.earnings[0].amount_based_on_formula = 1
sal_struct.earnings[0].formula = "base"
sal_struct.deductions = [sal_struct.deductions[0]]
sal_struct.deductions[0].amount_based_on_formula = 1
sal_struct.deductions[0].condition = "gross_pay > 100"
sal_struct.deductions[0].formula = "gross_pay * 0.2"
sal_struct.submit()
assignment = create_salary_structure_assignment(emp, "Salary Structure 2")
ss = make_salary_slip(sal_struct.name, employee = emp)
self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount)
def test_amount_totals(self): def test_amount_totals(self):
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"}) sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})

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 exp_claim.append("expenses", {
total_claim_amt=calc_service_exp() + fuel_price "expense_date": vehicle_log.date,
exp_claim.append("expenses",{ "description": _("Vehicle Expenses"),
"expense_date":vehicle_log.date, "amount": claim_amount
"description":_("Vehicle Expenses"),
"amount":total_claim_amt
}) })
return exp_claim.as_dict() return exp_claim.as_dict()

View File

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

View File

@@ -6,7 +6,6 @@ frappe.provide("erpnext.bom");
frappe.ui.form.on("BOM", { frappe.ui.form.on("BOM", {
setup: function(frm) { setup: function(frm) {
frm.custom_make_buttons = { frm.custom_make_buttons = {
'BOM': 'Duplicate BOM',
'Work Order': 'Work Order', 'Work Order': 'Work Order',
'Quality Inspection': 'Quality Inspection' 'Quality Inspection': 'Quality Inspection'
}; };
@@ -91,10 +90,6 @@ frappe.ui.form.on("BOM", {
} }
if(frm.doc.docstatus!=0) { if(frm.doc.docstatus!=0) {
frm.add_custom_button(__("Duplicate BOM"), function() {
frm.copy_doc();
}, __("Create"));
frm.add_custom_button(__("Work Order"), function() { frm.add_custom_button(__("Work Order"), function() {
frm.trigger("make_work_order"); frm.trigger("make_work_order");
}, __("Create")); }, __("Create"));
@@ -140,6 +135,7 @@ frappe.ui.form.on("BOM", {
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: {
bom_no: frm.doc.name,
item: frm.doc.item, item: frm.doc.item,
qty: data.qty || 0.0, qty: data.qty || 0.0,
project: frm.doc.project project: frm.doc.project

View File

@@ -487,6 +487,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()
@@ -815,6 +823,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

@@ -23,7 +23,5 @@ def get_data():
'label': _('Subcontract'), 'label': _('Subcontract'),
'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
} }
], ]
'disable_create_buttons': ["Item", "Purchase Order", "Purchase Receipt",
"Purchase Invoice", "Job Card", "Stock Entry"]
} }

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

@@ -7,6 +7,8 @@ frappe.ui.form.on('Job Card', {
if(frm.doc.docstatus == 0) { if(frm.doc.docstatus == 0) {
frm.set_df_property("operation", "read_only", frm.doc.operation_id ? 1 : 0); frm.set_df_property("operation", "read_only", frm.doc.operation_id ? 1 : 0);
} }
frappe.flags.pause_job = 0;
frappe.flags.resume_job = 0;
if(!frm.doc.__islocal && frm.doc.items && frm.doc.items.length) { if(!frm.doc.__islocal && frm.doc.items && frm.doc.items.length) {
if (frm.doc.for_quantity != frm.doc.transferred_qty) { if (frm.doc.for_quantity != frm.doc.transferred_qty) {
@@ -18,44 +20,103 @@ frappe.ui.form.on('Job Card', {
if (frm.doc.for_quantity != frm.doc.transferred_qty) { if (frm.doc.for_quantity != frm.doc.transferred_qty) {
frm.add_custom_button(__("Material Transfer"), () => { frm.add_custom_button(__("Material Transfer"), () => {
frm.trigger("make_stock_entry"); frm.trigger("make_stock_entry");
}); }).addClass("btn-primary");
} }
} }
if (frm.doc.docstatus == 0) { if (frm.doc.docstatus == 0 && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
frm.trigger("make_dashboard"); && (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
frm.trigger("prepare_timer_buttons");
}
},
if (!frm.doc.job_started) { prepare_timer_buttons: function(frm) {
frm.add_custom_button(__("Start Job"), () => { frm.trigger("make_dashboard");
let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs'); if (!frm.doc.job_started) {
row.from_time = frappe.datetime.now_datetime(); frm.add_custom_button(__("Start"), () => {
frm.set_value('job_started', 1); if (!frm.doc.employee) {
frm.set_value('started_time' , row.from_time); frappe.prompt({fieldtype: 'Link', label: __('Employee'), options: "Employee",
frm.save(); fieldname: 'employee'}, d => {
}); if (d.employee) {
} else { frm.set_value("employee", d.employee);
frm.add_custom_button(__("Complete Job"), () => {
let completed_time = frappe.datetime.now_datetime();
frm.doc.time_logs.forEach(d => {
if (d.from_time && !d.to_time) {
d.to_time = completed_time;
frm.set_value('started_time' , '');
frm.set_value('job_started', 0);
frm.save();
} }
})
}); frm.events.start_job(frm);
} }, __("Enter Value"), __("Start"));
} else {
frm.events.start_job(frm);
}
}).addClass("btn-primary");
} else if (frm.doc.status == "On Hold") {
frm.add_custom_button(__("Resume"), () => {
frappe.flags.resume_job = 1;
frm.events.start_job(frm);
}).addClass("btn-primary");
} else {
frm.add_custom_button(__("Pause"), () => {
frappe.flags.pause_job = 1;
frm.set_value("status", "On Hold");
frm.events.complete_job(frm);
});
frm.add_custom_button(__("Complete"), () => {
let completed_time = frappe.datetime.now_datetime();
frm.trigger("hide_timer");
if (frm.doc.for_quantity) {
frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'),
fieldname: 'qty', reqd: 1, default: frm.doc.for_quantity}, data => {
frm.events.complete_job(frm, completed_time, data.qty);
}, __("Enter Value"), __("Complete"));
} else {
frm.events.complete_job(frm, completed_time, 0);
}
}).addClass("btn-primary");
} }
}, },
start_job: function(frm) {
let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs');
row.from_time = frappe.datetime.now_datetime();
frm.set_value('job_started', 1);
frm.set_value('started_time' , row.from_time);
frm.set_value("status", "Work In Progress");
if (!frappe.flags.resume_job) {
frm.set_value('current_time' , 0);
}
frm.save();
},
complete_job: function(frm, completed_time, completed_qty) {
frm.doc.time_logs.forEach(d => {
if (d.from_time && !d.to_time) {
d.to_time = completed_time || frappe.datetime.now_datetime();
d.completed_qty = completed_qty || 0;
if(frappe.flags.pause_job) {
let currentIncrement = moment(d.to_time).diff(moment(d.from_time),"seconds") || 0;
frm.set_value('current_time' , currentIncrement + (frm.doc.current_time || 0));
} else {
frm.set_value('started_time' , '');
frm.set_value('job_started', 0);
frm.set_value('current_time' , 0);
}
frm.save();
}
});
},
make_dashboard: function(frm) { make_dashboard: function(frm) {
if(frm.doc.__islocal) if(frm.doc.__islocal)
return; return;
frm.dashboard.refresh(); frm.dashboard.refresh();
const timer = ` const timer = `
<div class="stopwatch" style="font-weight:bold"> <div class="stopwatch" style="font-weight:bold;margin:0px 13px 0px 2px;
color:#545454;font-size:18px;display:inline-block;vertical-align:text-bottom;>
<span class="hours">00</span> <span class="hours">00</span>
<span class="colon">:</span> <span class="colon">:</span>
<span class="minutes">00</span> <span class="minutes">00</span>
@@ -63,11 +124,16 @@ frappe.ui.form.on('Job Card', {
<span class="seconds">00</span> <span class="seconds">00</span>
</div>`; </div>`;
var section = frm.dashboard.add_section(timer); var section = frm.toolbar.page.add_inner_message(timer);
if (frm.doc.started_time) { let currentIncrement = frm.doc.current_time || 0;
let currentIncrement = moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds"); if (frm.doc.started_time || frm.doc.current_time) {
initialiseTimer(); if (frm.doc.status == "On Hold") {
updateStopwatch(currentIncrement);
} else {
currentIncrement += moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds");
initialiseTimer();
}
function initialiseTimer() { function initialiseTimer() {
const interval = setInterval(function() { const interval = setInterval(function() {
@@ -93,6 +159,10 @@ frappe.ui.form.on('Job Card', {
} }
}, },
hide_timer: function(frm) {
frm.toolbar.page.inner_toolbar.find(".stopwatch").remove();
},
for_quantity: function(frm) { for_quantity: function(frm) {
frm.doc.items = []; frm.doc.items = [];
frm.call({ frm.call({
@@ -122,5 +192,22 @@ frappe.ui.form.on('Job Card', {
timer: function(frm) { timer: function(frm) {
return `<button> Start </button>` return `<button> Start </button>`
},
set_total_completed_qty: function(frm) {
frm.doc.total_completed_qty = 0;
frm.doc.time_logs.forEach(d => {
if (d.completed_qty) {
frm.doc.total_completed_qty += d.completed_qty;
}
});
refresh_field("total_completed_qty");
} }
}); });
frappe.ui.form.on('Job Card Time Log', {
completed_qty: function(frm) {
frm.events.set_total_completed_qty(frm);
}
})

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt, time_diff_in_hours, get_datetime from frappe.utils import flt, time_diff_in_hours, get_datetime, time_diff
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document from frappe.model.document import Document
@@ -92,11 +92,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(_("Total completed qty can not be greater than for quantity"))
def update_work_order(self): def update_work_order(self):
if not self.work_order: if not self.work_order:
@@ -105,27 +103,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()
@@ -172,6 +177,8 @@ class JobCard(Document):
self.set_status(update_status) self.set_status(update_status)
def set_status(self, update_status=False): def set_status(self, update_status=False):
if self.status == "On Hold": return
self.status = { self.status = {
0: "Open", 0: "Open",
1: "Submitted", 1: "Submitted",
@@ -230,6 +237,7 @@ def make_stock_entry(source_name, target_doc=None):
target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0) target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0)
target.calculate_rate_and_amount() target.calculate_rate_and_amount()
target.set_missing_values() target.set_missing_values()
target.set_stock_entry_type()
doclist = get_mapped_doc("Job Card", source_name, { doclist = get_mapped_doc("Job Card", source_name, {
"Job Card": { "Job Card": {
@@ -251,3 +259,48 @@ def make_stock_entry(source_name, target_doc=None):
}, target_doc, set_missing_values) }, target_doc, set_missing_values)
return doclist return doclist
def time_diff_in_minutes(string_ed_date, string_st_date):
return time_diff(string_ed_date, string_st_date).total_seconds() / 60
@frappe.whitelist()
def get_job_details(start, end, filters=None):
events = []
event_color = {
"Completed": "#cdf5a6",
"Material Transferred": "#ffdd9e",
"Work In Progress": "#D3D3D3"
}
from frappe.desk.reportview import get_filters_cond
conditions = get_filters_cond("Job Card", filters, [])
job_cards = frappe.db.sql(""" SELECT `tabJob Card`.name, `tabJob Card`.work_order,
`tabJob Card`.employee_name, `tabJob Card`.status, ifnull(`tabJob Card`.remarks, ''),
min(`tabJob Card Time Log`.from_time) as from_time,
max(`tabJob Card Time Log`.to_time) as to_time
FROM `tabJob Card` , `tabJob Card Time Log`
WHERE
`tabJob Card`.name = `tabJob Card Time Log`.parent {0}
group by `tabJob Card`.name""".format(conditions), as_dict=1)
for d in job_cards:
subject_data = []
for field in ["name", "work_order", "remarks", "employee_name"]:
if not d.get(field): continue
subject_data.append(d.get(field))
color = event_color.get(d.status)
job_card_data = {
'from_time': d.from_time,
'to_time': d.to_time,
'name': d.name,
'subject': '\n'.join(subject_data),
'color': color if color else "#89bcde"
}
events.append(job_card_data)
return events

View File

@@ -0,0 +1,21 @@
frappe.views.calendar["Job Card"] = {
field_map: {
"start": "from_time",
"end": "to_time",
"id": "name",
"title": "subject",
"color": "color",
"allDay": "allDay",
"progress": "progress"
},
gantt: true,
filters: [
{
"fieldtype": "Link",
"fieldname": "employee",
"options": "Employee",
"label": __("Employee")
}
],
get_events_method: "erpnext.manufacturing.doctype.job_card.job_card.get_job_details"
};

View File

@@ -1,208 +1,57 @@
{ {
"allow_copy": 0, "creation": "2019-03-08 23:56:43.187569",
"allow_events_in_timeline": 0, "doctype": "DocType",
"allow_guest_to_view": 0, "editable_grid": 1,
"allow_import": 0, "engine": "InnoDB",
"allow_rename": 0, "field_order": [
"beta": 0, "from_time",
"creation": "2019-03-08 23:56:43.187569", "to_time",
"custom": 0, "column_break_2",
"docstatus": 0, "time_in_mins",
"doctype": "DocType", "completed_qty"
"document_type": "", ],
"editable_grid": 1,
"engine": "InnoDB",
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "fieldname": "from_time",
"allow_in_quick_entry": 0, "fieldtype": "Datetime",
"allow_on_submit": 0, "in_list_view": 1,
"bold": 0, "label": "From Time"
"collapsible": 0, },
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "from_time",
"fieldtype": "Datetime",
"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": "From Time",
"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, "fieldname": "to_time",
"allow_in_quick_entry": 0, "fieldtype": "Datetime",
"allow_on_submit": 0, "in_list_view": 1,
"bold": 0, "label": "To Time"
"collapsible": 0, },
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "to_time",
"fieldtype": "Datetime",
"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": "To Time",
"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, "fieldname": "column_break_2",
"allow_in_quick_entry": 0, "fieldtype": "Column Break"
"allow_on_submit": 0, },
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "fieldname": "time_in_mins",
"allow_in_quick_entry": 0, "fieldtype": "Float",
"allow_on_submit": 0, "in_list_view": 1,
"bold": 0, "label": "Time In Mins",
"collapsible": 0, "read_only": 1
"columns": 0, },
"fetch_if_empty": 0,
"fieldname": "time_in_mins",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Time In Mins",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_in_quick_entry": 0, "fieldname": "completed_qty",
"allow_on_submit": 0, "fieldtype": "Float",
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Completed Qty",
"columns": 0, "reqd": 1
"default": "0",
"fetch_if_empty": 0,
"fieldname": "completed_qty",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Completed Qty",
"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,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "istable": 1,
"hide_heading": 0, "modified": "2019-12-03 12:56:02.285448",
"hide_toolbar": 0, "modified_by": "Administrator",
"idx": 0, "module": "Manufacturing",
"image_view": 0, "name": "Job Card Time Log",
"in_create": 0, "owner": "Administrator",
"is_submittable": 0, "permissions": [],
"issingle": 0, "quick_entry": 1,
"istable": 1, "sort_field": "modified",
"max_attachments": 0, "sort_order": "ASC",
"modified": "2019-03-10 17:08:46.504910", "track_changes": 1
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card Time Log",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
} }

View File

@@ -71,12 +71,13 @@ frappe.ui.form.on('Production Plan', {
}, __('Create')); }, __('Create'));
} }
frm.page.set_inner_btn_group_as_primary(__('Create'));
frm.trigger("material_requirement"); frm.trigger("material_requirement");
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;"> const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
<tr><td style="padding-left:25px"> <tr><td style="padding-left:25px">
<div> <div>
<h3> <h3 style="text-decoration: underline;">
<a href = "https://erpnext.com/docs/user/manual/en/stock/projected-quantity"> <a href = "https://erpnext.com/docs/user/manual/en/stock/projected-quantity">
${__("Projected Quantity Formula")} ${__("Projected Quantity Formula")}
</a> </a>

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

@@ -6,7 +6,7 @@ def get_data():
'fieldname': 'production_plan', 'fieldname': 'production_plan',
'transactions': [ 'transactions': [
{ {
'label': _('Related'), 'label': _('Transactions'),
'items': ['Work Order', 'Material Request'] 'items': ['Work Order', 'Material Request']
}, },
] ]

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")
@@ -368,7 +400,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

View File

@@ -6,7 +6,7 @@ frappe.ui.form.on("Work Order", {
frm.custom_make_buttons = { frm.custom_make_buttons = {
'Stock Entry': 'Start', 'Stock Entry': 'Start',
'Pick List': 'Create Pick List', 'Pick List': 'Create Pick List',
'Job Card': 'Create Job Card', 'Job Card': 'Create Job Card'
}; };
// Set query for warehouses // Set query for warehouses
@@ -132,7 +132,8 @@ frappe.ui.form.on("Work Order", {
} }
if (frm.doc.docstatus===1) { if (frm.doc.docstatus===1) {
frm.trigger('show_progress'); frm.trigger('show_progress_for_items');
frm.trigger('show_progress_for_operations');
} }
if (frm.doc.docstatus === 1 if (frm.doc.docstatus === 1
@@ -180,89 +181,72 @@ frappe.ui.form.on("Work Order", {
make_job_card: function(frm) { make_job_card: function(frm) {
let qty = 0; let qty = 0;
const fields = [{ let operations_data = [];
fieldtype: "Link",
fieldname: "operation",
options: "Operation",
label: __("Operation"),
get_query: () => {
const filter_workstation = frm.doc.operations.filter(d => {
if (d.status != "Completed") {
return d;
}
});
return { const dialog = frappe.prompt({fieldname: 'operations', fieldtype: 'Table', label: __('Operations'),
filters: { fields: [
name: ["in", (filter_workstation || []).map(d => d.operation)] {
} fieldtype:'Link',
}; fieldname:'operation',
}, label: __('Operation'),
reqd: true read_only:1,
}, { in_list_view:1
fieldtype: "Link", },
fieldname: "workstation", {
options: "Workstation", fieldtype:'Link',
label: __("Workstation"), fieldname:'workstation',
get_query: () => { label: __('Workstation'),
const operation = dialog.get_value("operation"); read_only:1,
const filter_workstation = frm.doc.operations.filter(d => { in_list_view:1
if (d.operation == operation) { },
return d; {
} fieldtype:'Data',
}); fieldname:'name',
label: __('Operation Id')
return { },
filters: { {
name: ["in", (filter_workstation || []).map(d => d.workstation)] fieldtype:'Float',
} fieldname:'pending_qty',
}; label: __('Pending Qty'),
}, },
onchange: () => { {
const operation = dialog.get_value("operation"); fieldtype:'Float',
const workstation = dialog.get_value("workstation"); fieldname:'qty',
if (operation && workstation) { label: __('Quantity to Manufacture'),
const row = frm.doc.operations.filter(d => d.operation == operation && d.workstation == workstation)[0]; read_only:0,
qty = frm.doc.qty - row.completed_qty; in_list_view:1,
},
if (qty > 0) { ],
dialog.set_value("qty", qty); data: operations_data,
} in_place_edit: true,
} get_data: function() {
}, return operations_data;
reqd: true
}, {
fieldtype: "Float",
fieldname: "qty",
label: __("For Quantity"),
reqd: true
}];
const dialog = frappe.prompt(fields, function(data) {
if (data.qty > qty) {
frappe.throw(__("For Quantity must be less than quantity {0}", [qty]));
} }
}, function(data) {
if (data.qty <= 0) {
frappe.throw(__("For Quantity must be greater than zero"));
}
frappe.call({ frappe.call({
method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card", method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card",
args: { args: {
work_order: frm.doc.name, work_order: frm.doc.name,
operation: data.operation, operations: data.operations,
workstation: data.workstation,
qty: data.qty
},
callback: function(r){
if (r.message) {
var doc = frappe.model.sync(r.message)[0];
frappe.set_route("Form", doc.doctype, doc.name);
}
} }
}); });
}, __("For Job Card")); }, __("Job Card"), __("Create"));
var pending_qty = 0;
frm.doc.operations.forEach(data => {
if(data.completed_qty != frm.doc.qty) {
pending_qty = frm.doc.qty - flt(data.completed_qty);
dialog.fields_dict.operations.df.data.push({
'name': data.name,
'operation': data.operation,
'workstation': data.workstation,
'qty': pending_qty,
'pending_qty': pending_qty,
});
}
});
dialog.fields_dict.operations.grid.refresh();
}, },
make_bom: function(frm) { make_bom: function(frm) {
@@ -278,7 +262,7 @@ frappe.ui.form.on("Work Order", {
}); });
}, },
show_progress: function(frm) { show_progress_for_items: function(frm) {
var bars = []; var bars = [];
var message = ''; var message = '';
var added_min = false; var added_min = false;
@@ -312,6 +296,44 @@ frappe.ui.form.on("Work Order", {
frm.dashboard.add_progress(__('Status'), bars, message); frm.dashboard.add_progress(__('Status'), bars, message);
}, },
show_progress_for_operations: function(frm) {
if (frm.doc.operations && frm.doc.operations.length) {
let progress_class = {
"Work in Progress": "progress-bar-warning",
"Completed": "progress-bar-success"
};
let bars = [];
let message = '';
let title = '';
let status_wise_oprtation_data = {};
let total_completed_qty = frm.doc.qty * frm.doc.operations.length;
frm.doc.operations.forEach(d => {
if (!status_wise_oprtation_data[d.status]) {
status_wise_oprtation_data[d.status] = [d.completed_qty, d.operation];
} else {
status_wise_oprtation_data[d.status][0] += d.completed_qty;
status_wise_oprtation_data[d.status][1] += ', ' + d.operation;
}
});
for (let key in status_wise_oprtation_data) {
title = __("{0} Operations: {1}", [key, status_wise_oprtation_data[key][1].bold()]);
bars.push({
'title': title,
'width': status_wise_oprtation_data[key][0] / total_completed_qty * 100 + '%',
'progress_class': progress_class[key]
});
message += title + '. ';
}
frm.dashboard.add_progress(__('Status'), bars, message);
}
},
production_item: function(frm) { production_item: function(frm) {
if (frm.doc.production_item) { if (frm.doc.production_item) {
frappe.call({ frappe.call({

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2013-01-10 16:34:16", "creation": "2013-01-10 16:34:16",
@@ -468,7 +469,8 @@
"idx": 1, "idx": 1,
"image_field": "image", "image_field": "image",
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-08-28 12:29:35.315239", "links": [],
"modified": "2019-12-04 11:20:04.695123",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Work Order", "name": "Work Order",

View File

@@ -6,7 +6,7 @@ import frappe
import json import json
import math import math
from frappe import _ from frappe import _
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
@@ -274,7 +274,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, {
@@ -429,6 +429,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()
@@ -436,9 +439,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:
@@ -609,7 +609,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)
@@ -618,6 +618,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)
@@ -711,21 +712,41 @@ def query_sales_order(production_item):
return out return out
@frappe.whitelist() @frappe.whitelist()
def make_job_card(work_order, operation, workstation, qty=0): def make_job_card(work_order, operations):
if isinstance(operations, string_types):
operations = json.loads(operations)
work_order = frappe.get_doc('Work Order', work_order) work_order = frappe.get_doc('Work Order', work_order)
row = get_work_order_operation_data(work_order, operation, workstation) for row in operations:
if row: validate_operation_data(row)
return create_job_card(work_order, row, qty) create_job_card(work_order, row, row.get("qty"), auto_create=True)
def validate_operation_data(row):
if row.get("qty") <= 0:
frappe.throw(_("Quantity to Manufacture can not be zero for the operation {0}")
.format(
frappe.bold(row.get("operation"))
)
)
if row.get("qty") > row.get("pending_qty"):
frappe.throw(_("For operation {0}: Quantity ({1}) can not be greter than pending quantity({2})")
.format(
frappe.bold(row.get("operation")),
frappe.bold(row.get("qty")),
frappe.bold(row.get("pending_qty"))
)
)
def create_job_card(work_order, row, qty=0, auto_create=False): def create_job_card(work_order, row, qty=0, auto_create=False):
doc = frappe.new_doc("Job Card") doc = frappe.new_doc("Job Card")
doc.update({ doc.update({
'work_order': work_order.name, 'work_order': work_order.name,
'operation': row.operation, 'operation': row.get("operation"),
'workstation': row.workstation, 'workstation': row.get("workstation"),
'posting_date': nowdate(), 'posting_date': nowdate(),
'for_quantity': qty or work_order.get('qty', 0), 'for_quantity': qty or work_order.get('qty', 0),
'operation_id': row.name, 'operation_id': row.get("name"),
'bom_no': work_order.bom_no, 'bom_no': work_order.bom_no,
'project': work_order.project, 'project': work_order.project,
'company': work_order.company, 'company': work_order.company,
@@ -738,7 +759,7 @@ def create_job_card(work_order, row, qty=0, auto_create=False):
if auto_create: if auto_create:
doc.flags.ignore_mandatory = True doc.flags.ignore_mandatory = True
doc.insert() doc.insert()
frappe.msgprint(_("Job card {0} created").format(doc.name)) frappe.msgprint(_("Job card {0} created").format(get_link_to_form("Job Card", doc.name)))
return doc return doc

View File

@@ -2,11 +2,13 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.views.calendar["Work Order"] = { frappe.views.calendar["Work Order"] = {
fields: ["planned_start_date", "planned_end_date", "status", "produced_qty", "qty", "name", "name"],
field_map: { field_map: {
"start": "planned_start_date", "start": "planned_start_date",
"end": "planned_end_date", "end": "planned_end_date",
"id": "name", "id": "name",
"title": "name", "title": "name",
"status": "status",
"allDay": "allDay", "allDay": "allDay",
"progress": function(data) { "progress": function(data) {
return flt(data.produced_qty) / data.qty * 100; return flt(data.produced_qty) / data.qty * 100;

View File

@@ -6,7 +6,8 @@ def get_data():
'fieldname': 'work_order', 'fieldname': 'work_order',
'transactions': [ 'transactions': [
{ {
'items': ['Pick List', 'Stock Entry', 'Job Card'] 'label': _('Transactions'),
'items': ['Stock Entry', 'Job Card', 'Pick List']
} }
] ]
} }

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"creation": "2014-10-16 14:35:41.950175", "creation": "2014-10-16 14:35:41.950175",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@@ -68,6 +69,7 @@
"description": "Operation completed for how many finished goods?", "description": "Operation completed for how many finished goods?",
"fieldname": "completed_qty", "fieldname": "completed_qty",
"fieldtype": "Float", "fieldtype": "Float",
"in_list_view": 1,
"label": "Completed Qty", "label": "Completed Qty",
"no_copy": 1, "no_copy": 1,
"read_only": 1 "read_only": 1
@@ -188,8 +190,9 @@
} }
], ],
"istable": 1, "istable": 1,
"modified": "2019-07-16 23:01:07.720337", "links": [],
"modified_by": "govindsmenokee@gmail.com", "modified": "2019-12-03 19:24:29.594189",
"modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Work Order Operation", "name": "Work Order Operation",
"owner": "Administrator", "owner": "Administrator",
@@ -197,4 +200,4 @@
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 1 "track_changes": 1
} }

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

@@ -4,6 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils.data import comma_and
def execute(filters=None): def execute(filters=None):
# if not filters: filters = {} # if not filters: filters = {}
@@ -13,35 +14,36 @@ def execute(filters=None):
data = get_bom_stock(filters) data = get_bom_stock(filters)
qty_to_make = filters.get("qty_to_make") qty_to_make = filters.get("qty_to_make")
manufacture_details = get_manufacturer_records()
for row in data: for row in data:
item_map = get_item_details(row.item_code)
reqd_qty = qty_to_make * row.actual_qty reqd_qty = qty_to_make * row.actual_qty
last_pur_price = frappe.db.get_value("Item", row.item_code, "last_purchase_rate") last_pur_price = frappe.db.get_value("Item", row.item_code, "last_purchase_rate")
if row.to_build > 0:
diff_qty = row.to_build - reqd_qty
summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, row.to_build, reqd_qty, diff_qty, last_pur_price])
else:
diff_qty = 0 - reqd_qty
summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, "0.000", reqd_qty, diff_qty, last_pur_price])
summ_data.append(get_report_data(last_pur_price, reqd_qty, row, manufacture_details))
return columns, summ_data return columns, summ_data
def get_report_data(last_pur_price, reqd_qty, row, manufacture_details):
to_build = row.to_build if row.to_build > 0 else 0
diff_qty = to_build - reqd_qty
return [row.item_code, row.description,
comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', [])),
comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', [])),
row.actual_qty, str(to_build),
reqd_qty, diff_qty, last_pur_price]
def get_columns(): def get_columns():
"""return columns""" """return columns"""
columns = [ columns = [
_("Item") + ":Link/Item:100", _("Item") + ":Link/Item:100",
_("Description") + "::150", _("Description") + "::150",
_("Manufacturer") + "::100", _("Manufacturer") + "::250",
_("Manufacturer Part Number") + "::100", _("Manufacturer Part Number") + "::250",
_("Qty") + ":Float:50", _("Qty") + ":Float:50",
_("Stock Qty") + ":Float:100", _("Stock Qty") + ":Float:100",
_("Reqd Qty")+ ":Float:100", _("Reqd Qty")+ ":Float:100",
_("Diff Qty")+ ":Float:100", _("Diff Qty")+ ":Float:100",
_("Last Purchase Price")+ ":Float:100", _("Last Purchase Price")+ ":Float:100",
] ]
return columns return columns
def get_bom_stock(filters): def get_bom_stock(filters):
@@ -85,7 +87,12 @@ def get_bom_stock(filters):
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1) GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
def get_item_details(item_code): def get_manufacturer_records():
items = frappe.db.sql("""select it.item_group, it.item_name, it.stock_uom, it.name, it.brand, it.description, it.manufacturer_part_no, it.manufacturer from tabItem it where it.item_code = %s""", item_code, as_dict=1) details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"])
manufacture_details = frappe._dict()
for detail in details:
dic = manufacture_details.setdefault(detail.get('parent'), {})
dic.setdefault('manufacturer', []).append(detail.get('manufacturer'))
dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no'))
return dict((d.name, d) for d in items) return manufacture_details

View File

@@ -582,7 +582,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
@@ -627,10 +627,11 @@ erpnext.patches.v12_0.update_ewaybill_field_position
erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes
erpnext.patches.v11_1.set_status_for_material_request_type_manufacture erpnext.patches.v11_1.set_status_for_material_request_type_manufacture
erpnext.patches.v12_0.move_plaid_settings_to_doctype erpnext.patches.v12_0.move_plaid_settings_to_doctype
execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_link') 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
@@ -651,3 +652,5 @@ erpnext.patches.v12_0.add_export_type_field_in_party_master
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.create_irs_1099_field_united_states erpnext.patches.v12_0.create_irs_1099_field_united_states
erpnext.patches.v12_0.set_correct_status_for_expense_claim erpnext.patches.v12_0.set_correct_status_for_expense_claim
erpnext.patches.v12_0.set_permission_einvoicing
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 @@
import frappe
from erpnext.regional.italy.setup import make_custom_fields
from frappe.permissions import add_permission, update_permission_property
def execute():
company = frappe.get_all('Company', filters = {'country': 'Italy'})
if not company:
return
make_custom_fields()
add_permission('Import Supplier Invoice', 'Accounts Manager', 0)
update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'write', 1)
update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'create', 1)

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

@@ -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",
@@ -361,11 +362,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:26:04.788061",
"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

@@ -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;
@@ -506,6 +521,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}, },
() => me.frm.script_manager.trigger("price_list_rate", cdt, cdn), () => me.frm.script_manager.trigger("price_list_rate", cdt, cdn),
() => 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];
@@ -515,7 +539,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,23 +1,25 @@
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"];
frappe.call({ 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)
erpnext.dimension_filters.forEach((dimension) => { && default_dimensions[frm.doc.company]) {
if (frappe.meta.has_field(doctype, dimension['fieldname'])) { frm.trigger('update_dimension');
frm.set_value(dimension['fieldname'], erpnext.default_dimensions[frm.doc.company][dimension['document_type']]);
}
});
} }
}, },
update_dimension: function(frm) {
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'])) {
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();

View File

@@ -5,14 +5,13 @@ erpnext.SerialNoBatchSelector = Class.extend({
this.show_dialog = show_dialog; this.show_dialog = show_dialog;
// frm, item, warehouse_details, has_batch, oldest // frm, item, warehouse_details, has_batch, oldest
let d = this.item; let d = this.item;
if (d && d.has_batch_no && (!d.batch_no || this.show_dialog)) { this.has_batch = 0; this.has_serial_no = 0;
this.has_batch = 1;
this.setup(); if (d && d.has_batch_no && (!d.batch_no || this.show_dialog)) this.has_batch = 1;
// !(this.show_dialog == false) ensures that show_dialog is implictly true, even when undefined // !(this.show_dialog == false) ensures that show_dialog is implictly true, even when undefined
} else if(d && d.has_serial_no && !(this.show_dialog == false)) { if(d && d.has_serial_no && !(this.show_dialog == false)) this.has_serial_no = 1;
this.has_batch = 0;
this.setup(); this.setup();
}
}, },
setup: function() { setup: function() {
@@ -36,16 +35,16 @@ erpnext.SerialNoBatchSelector = Class.extend({
label: __('Item Code'), label: __('Item Code'),
default: me.item_code default: me.item_code
}, },
{fieldtype:'Column Break'},
{ {
fieldname: 'warehouse', fieldname: 'warehouse',
fieldtype:'Link', fieldtype:'Link',
options: 'Warehouse', options: 'Warehouse',
reqd: me.has_batch && !me.has_serial_no ? 0 : 1,
label: __(me.warehouse_details.type), label: __(me.warehouse_details.type),
default: me.warehouse_details.name, default: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '',
onchange: function(e) { onchange: function(e) {
if(me.has_batch) { if(me.has_batch && !me.has_serial_no) {
fields = fields.concat(me.get_batch_fields()); fields = fields.concat(me.get_batch_fields());
} else { } else {
fields = fields.concat(me.get_serial_no_fields()); fields = fields.concat(me.get_serial_no_fields());
@@ -74,15 +73,16 @@ erpnext.SerialNoBatchSelector = Class.extend({
{ {
fieldname: 'qty', fieldname: 'qty',
fieldtype:'Float', fieldtype:'Float',
read_only: me.has_batch, read_only: me.has_batch && !me.has_serial_no,
label: __(me.has_batch ? 'Total Qty' : 'Qty'), label: __(me.has_batch && !me.has_serial_no ? 'Total Qty' : 'Qty'),
default: 0 default: 0
}, },
{ {
fieldname: 'auto_fetch_button', fieldname: 'auto_fetch_button',
fieldtype:'Button', fieldtype:'Button',
hidden: me.has_batch, hidden: me.has_batch && !me.has_serial_no,
label: __('Fetch based on FIFO'), label: __('Auto Fetch'),
description: __('Fetch Serial Numbers based on FIFO'),
click: () => { click: () => {
let qty = this.dialog.fields_dict.qty.get_value(); let qty = this.dialog.fields_dict.qty.get_value();
let numbers = frappe.call({ let numbers = frappe.call({
@@ -90,7 +90,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
args: { args: {
qty: qty, qty: qty,
item_code: me.item_code, item_code: me.item_code,
warehouse: me.warehouse_details.name, warehouse: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '',
batch_no: me.item.batch_no || null batch_no: me.item.batch_no || null
} }
}); });
@@ -109,10 +109,12 @@ erpnext.SerialNoBatchSelector = Class.extend({
} }
]; ];
if (this.has_batch) { if (this.has_batch && !this.has_serial_no) {
title = __("Select Batch Numbers"); title = __("Select Batch Numbers");
fields = fields.concat(this.get_batch_fields()); fields = fields.concat(this.get_batch_fields());
} else { } else {
// if only serial no OR
// if both batch_no & serial_no then only select serial_no and auto set batches nos
title = __("Select Serial Numbers"); title = __("Select Serial Numbers");
fields = fields.concat(this.get_serial_no_fields()); fields = fields.concat(this.get_serial_no_fields());
} }
@@ -122,25 +124,31 @@ erpnext.SerialNoBatchSelector = Class.extend({
fields: fields fields: fields
}); });
if (this.item.serial_no) {
this.dialog.fields_dict.serial_no.set_value(this.item.serial_no);
}
this.dialog.set_primary_action(__('Insert'), function() { this.dialog.set_primary_action(__('Insert'), function() {
me.values = me.dialog.get_values(); me.values = me.dialog.get_values();
if(me.validate()) { if(me.validate()) {
me.set_items(); frappe.run_serially([
me.dialog.hide(); () => me.update_batch_items(),
() => me.update_serial_no_item(),
() => me.update_batch_serial_no_items(),
() => {
refresh_field("items");
if (me.callback) {
return me.callback(me.item);
}
},
() => me.dialog.hide()
])
} }
}); });
if(this.show_dialog) { if(this.show_dialog) {
let d = this.item; let d = this.item;
if (d.has_serial_no && d.serial_no) { if (this.item.serial_no) {
this.dialog.set_value('serial_no', d.serial_no); this.dialog.fields_dict.serial_no.set_value(this.item.serial_no);
} }
if (d.has_batch_no && d.batch_no) { if (this.has_batch && !this.has_serial_no && d.batch_no) {
this.frm.doc.items.forEach(data => { this.frm.doc.items.forEach(data => {
if(data.item_code == d.item_code) { if(data.item_code == d.item_code) {
this.dialog.fields_dict.batches.df.data.push({ this.dialog.fields_dict.batches.df.data.push({
@@ -155,7 +163,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
} }
} }
if (this.has_batch) { if (this.has_batch && !this.has_serial_no) {
this.update_total_qty(); this.update_total_qty();
} }
@@ -174,7 +182,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
frappe.throw(__("Please select a warehouse")); frappe.throw(__("Please select a warehouse"));
return false; return false;
} }
if(this.has_batch) { if(this.has_batch && !this.has_serial_no) {
if(values.batches.length === 0 || !values.batches) { if(values.batches.length === 0 || !values.batches) {
frappe.throw(__("Please select batches for batched item " frappe.throw(__("Please select batches for batched item "
+ values.item_code)); + values.item_code));
@@ -193,34 +201,23 @@ erpnext.SerialNoBatchSelector = Class.extend({
} else { } else {
let serial_nos = values.serial_no || ''; let serial_nos = values.serial_no || '';
if (!serial_nos || !serial_nos.replace(/\s/g, '').length) { if (!serial_nos || !serial_nos.replace(/\s/g, '').length) {
if (!this.show_dialog) { frappe.throw(__("Please enter serial numbers for serialized item "
frappe.throw(__("Please enter serial numbers for serialized item " + values.item_code));
+ values.item_code)); return false;
return false;
}
} }
return true; return true;
} }
}, },
set_items: function() { update_batch_items() {
var me = this; // clones an items if muliple batches are selected.
if(this.has_batch) { if(this.has_batch && !this.has_serial_no) {
this.values.batches.map((batch, i) => { this.values.batches.map((batch, i) => {
let batch_no = batch.batch_no; let batch_no = batch.batch_no;
let row = ''; let row = '';
if (i !== 0 && !this.batch_exists(batch_no)) { if (i !== 0 && !this.batch_exists(batch_no)) {
row = this.frm.add_child("items", { row = this.frm.add_child("items", { ...this.item });
'item_code': this.item.item_code,
'item_name': this.item.item_name,
'price_list_rate': this.item.price_list_rate,
'rate': this.item.rate,
'qty': batch.selected_qty,
'batch_no': batch_no,
'actual_qty': this.item.actual_qty,
'discount_percentage': this.item.discount_percentage
});
} else { } else {
row = this.frm.doc.items.find(i => i.batch_no === batch_no); row = this.frm.doc.items.find(i => i.batch_no === batch_no);
} }
@@ -228,16 +225,59 @@ erpnext.SerialNoBatchSelector = Class.extend({
if (!row) { if (!row) {
row = this.item; row = this.item;
} }
// this ensures that qty & batch no is set
this.map_row_values(row, batch, 'batch_no', this.map_row_values(row, batch, 'batch_no',
'selected_qty', this.values.warehouse); 'selected_qty', this.values.warehouse);
}); });
} else { }
},
update_serial_no_item() {
// just updates serial no for the item
if(this.has_serial_no && !this.has_batch) {
this.map_row_values(this.item, this.values, 'serial_no', 'qty'); this.map_row_values(this.item, this.values, 'serial_no', 'qty');
} }
},
refresh_field("items"); update_batch_serial_no_items() {
this.callback && this.callback(this.item); // if serial no selected is from different batches, adds new rows for each batch.
if(this.has_batch && this.has_serial_no) {
const selected_serial_nos = this.values.serial_no.split(/\n/g).filter(s => s);
return frappe.db.get_list("Serial No", {
filters: { 'name': ["in", selected_serial_nos]},
fields: ["batch_no", "name"]
}).then((data) => {
// data = [{batch_no: 'batch-1', name: "SR-001"},
// {batch_no: 'batch-2', name: "SR-003"}, {batch_no: 'batch-2', name: "SR-004"}]
const batch_serial_map = data.reduce((acc, d) => {
if (!acc[d['batch_no']]) acc[d['batch_no']] = [];
acc[d['batch_no']].push(d['name'])
return acc
}, {})
// batch_serial_map = { "batch-1": ['SR-001'], "batch-2": ["SR-003", "SR-004"]}
Object.keys(batch_serial_map).map((batch_no, i) => {
let row = '';
const serial_no = batch_serial_map[batch_no];
if (i == 0) {
row = this.item;
this.map_row_values(row, {qty: serial_no.length, batch_no: batch_no}, 'batch_no',
'qty', this.values.warehouse);
} else if (!this.batch_exists(batch_no)) {
row = this.frm.add_child("items", { ...this.item });
row.batch_no = batch_no;
} else {
row = this.frm.doc.items.find(i => i.batch_no === batch_no);
}
const values = {
'qty': serial_no.length,
'serial_no': serial_no.join('\n')
}
this.map_row_values(row, values, 'serial_no',
'qty', this.values.warehouse);
});
})
}
}, },
batch_exists: function(batch) { batch_exists: function(batch) {
@@ -287,7 +327,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
return { return {
filters: { filters: {
item_code: me.item_code, item_code: me.item_code,
warehouse: me.warehouse || me.warehouse_details.name warehouse: me.warehouse || typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : ''
}, },
query: 'erpnext.controllers.queries.get_batch_no' query: 'erpnext.controllers.queries.get_batch_no'
}; };
@@ -313,11 +353,15 @@ erpnext.SerialNoBatchSelector = Class.extend({
frappe.throw(__(`Batch ${val} already selected.`)); frappe.throw(__(`Batch ${val} already selected.`));
return; return;
} }
let batch_number = me.item.batch_no ||
this.grid_row.on_grid_fields_dict.batch_no.get_value();
if (me.warehouse_details.name) { if (me.warehouse_details.name) {
frappe.call({ frappe.call({
method: 'erpnext.stock.doctype.batch.batch.get_batch_qty', method: 'erpnext.stock.doctype.batch.batch.get_batch_qty',
args: { args: {
batch_no: me.item.batch_no, batch_no: batch_number,
warehouse: me.warehouse_details.name, warehouse: me.warehouse_details.name,
item_code: me.item_code item_code: me.item_code
}, },
@@ -443,7 +487,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
{ {
fieldname: 'serial_no', fieldname: 'serial_no',
fieldtype: 'Small Text', fieldtype: 'Small Text',
label: __(me.has_batch ? 'Selected Batch Numbers' : 'Selected Serial Numbers'), label: __(me.has_batch && !me.has_serial_no ? 'Selected Batch Numbers' : 'Selected Serial Numbers'),
onchange: function() { onchange: function() {
me.serial_list = this.get_value() me.serial_list = this.get_value()
.replace(/\n/g, ' ').match(/\S+/g) || []; .replace(/\n/g, ' ').match(/\S+/g) || [];

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "format:PRC-{quality_procedure_name}", "autoname": "format:PRC-{quality_procedure_name}",
"creation": "2018-10-06 00:06:29.756804", "creation": "2018-10-06 00:06:29.756804",
"doctype": "DocType", "doctype": "DocType",
@@ -69,10 +70,13 @@
"reqd": 1 "reqd": 1
} }
], ],
"modified": "2019-08-05 13:09:29.945082", "is_tree": 1,
"links": [],
"modified": "2020-03-18 18:26:05.511984",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Procedure", "name": "Quality Procedure",
"nsm_parent_field": "parent_quality_procedure",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@@ -29,7 +29,7 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>(a) {{__("Outward taxable supplies(other than zero rated, nil rated and exempted")}}</td> <td>(a) {{__("Outward taxable supplies(other than zero rated, nil rated and exempted)")}}</td>
<td class="right">{{ flt(data.sup_details.osup_det.txval, 2) }}</td> <td class="right">{{ flt(data.sup_details.osup_det.txval, 2) }}</td>
<td class="right">{{ flt(data.sup_details.osup_det.iamt, 2) }}</td> <td class="right">{{ flt(data.sup_details.osup_det.iamt, 2) }}</td>
<td class="right">{{ flt(data.sup_details.osup_det.camt, 2) }}</td> <td class="right">{{ flt(data.sup_details.osup_det.camt, 2) }}</td>

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "format:GSTR3B-{month}-{year}-{company_address}", "autoname": "format:GSTR3B-{month}-{year}-{company_address}",
"creation": "2019-02-04 11:35:55.964639", "creation": "2019-02-04 11:35:55.964639",
"doctype": "DocType", "doctype": "DocType",
@@ -48,25 +49,13 @@
"read_only": 1 "read_only": 1
} }
], ],
"modified": "2019-08-10 22:30:26.727038", "links": [],
"modified": "2020-04-04 19:32:30.772908",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Regional", "module": "Regional",
"name": "GSTR 3B Report", "name": "GSTR 3B Report",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [],
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 1 "track_changes": 1

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