Merge branch 'develop' into customer-group-price-list-in-pos

This commit is contained in:
Deepesh Garg
2020-03-31 16:33:41 +05:30
committed by GitHub
67 changed files with 892 additions and 794 deletions

View File

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

View File

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

View File

@@ -2433,8 +2433,6 @@
"Erl\u00f6se aus Verk\u00e4ufen Sachanlageverm\u00f6gen (bei Buchgewinn)": { "Erl\u00f6se aus Verk\u00e4ufen Sachanlageverm\u00f6gen (bei Buchgewinn)": {
"account_number": "4849" "account_number": "4849"
}, },
"Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn) (Gruppe)": {
"is_group": 1,
"Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": { "Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": {
"account_number": "4850" "account_number": "4850"
}, },
@@ -2455,7 +2453,6 @@
}, },
"Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": { "Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": {
"account_number": "4858" "account_number": "4858"
}
}, },
"Ertr\u00e4ge aus Zuschreibungen des Sachanlageverm\u00f6gens": { "Ertr\u00e4ge aus Zuschreibungen des Sachanlageverm\u00f6gens": {
"account_number": "4910", "account_number": "4910",
@@ -2578,8 +2575,6 @@
"Entnahme von Gegenst\u00e4nden ohne USt": { "Entnahme von Gegenst\u00e4nden ohne USt": {
"account_number": "4605" "account_number": "4605"
}, },
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt (Gruppe)": {
"is_group": 1,
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": { "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": {
"account_number": "4630" "account_number": "4630"
}, },
@@ -2591,7 +2586,6 @@
}, },
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": { "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": {
"account_number": "4639" "account_number": "4639"
}
}, },
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 19 % USt (Gruppe)": { "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 19 % USt (Gruppe)": {
"is_group": 1, "is_group": 1,
@@ -2629,14 +2623,11 @@
"Unentgeltliche Zuwendung von Gegenst\u00e4nden ohne USt": { "Unentgeltliche Zuwendung von Gegenst\u00e4nden ohne USt": {
"account_number": "4689" "account_number": "4689"
}, },
"Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze) (Gruppe)": {
"is_group": 1,
"Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": { "Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": {
"account_number": "4690" "account_number": "4690"
}, },
"Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": { "Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": {
"account_number": "4695" "account_number": "4695"
}
}, },
"Au\u00dferordentliche Ertr\u00e4ge (Gruppe)": { "Au\u00dferordentliche Ertr\u00e4ge (Gruppe)": {
"is_group": 1, "is_group": 1,
@@ -2646,8 +2637,6 @@
"Au\u00dferordentliche Ertr\u00e4ge finanzwirksam": { "Au\u00dferordentliche Ertr\u00e4ge finanzwirksam": {
"account_number": "7401" "account_number": "7401"
}, },
"Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam (Gruppe)": {
"is_group": 1,
"Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": { "Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": {
"account_number": "7450" "account_number": "7450"
}, },
@@ -2662,10 +2651,7 @@
}, },
"Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": { "Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": {
"account_number": "7454" "account_number": "7454"
}
}, },
"Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften (Gruppe)": {
"is_group": 1,
"Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": { "Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": {
"account_number": "7460" "account_number": "7460"
}, },
@@ -2682,7 +2668,6 @@
"account_number": "7464" "account_number": "7464"
} }
} }
}
}, },
"7 - sonstige betriebliche Aufwendungen": { "7 - sonstige betriebliche Aufwendungen": {
"root_type": "Expense", "root_type": "Expense",
@@ -2718,6 +2703,7 @@
}, },
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": { "Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": {
"account_number": "4729" "account_number": "4729"
}
}, },
"Gew\u00e4hrte Skonti (Gruppe)": { "Gew\u00e4hrte Skonti (Gruppe)": {
"is_group": 1, "is_group": 1,
@@ -2752,6 +2738,8 @@
"account_number": "4748" "account_number": "4748"
} }
}, },
"Gew\u00e4hrte Boni (Gruppe)": {
"is_group": 1,
"Gew\u00e4hrte Boni 7 % USt": { "Gew\u00e4hrte Boni 7 % USt": {
"account_number": "4750" "account_number": "4750"
}, },
@@ -2864,8 +2852,6 @@
"account_number": "6398" "account_number": "6398"
} }
}, },
"Versicherungen (Gruppe)": {
"is_group": 1,
"Versicherungen": { "Versicherungen": {
"account_number": "6400" "account_number": "6400"
}, },
@@ -2913,54 +2899,32 @@
}, },
"Mietleasing (bewegliche Wirtschaftsg\u00fcter)": { "Mietleasing (bewegliche Wirtschaftsg\u00fcter)": {
"account_number": "6498" "account_number": "6498"
}
}, },
"Fahrzeugkosten (Gruppe)": { "Fahrzeugkosten (Gruppe)": {
"is_group": 1, "is_group": 1,
"Fahrzeugkosten": { "Fahrzeugkosten": {
"account_number": "6500" "account_number": "6500"
}, },
"Kfz-Versicherungen (Gruppe)": {
"is_group": 1,
"Kfz-Versicherungen": { "Kfz-Versicherungen": {
"account_number": "6520" "account_number": "6520"
}
}, },
"Laufende Kfz-Betriebskosten (Gruppe)": {
"is_group": 1,
"Laufende Kfz-Betriebskosten": { "Laufende Kfz-Betriebskosten": {
"account_number": "6530" "account_number": "6530"
}
}, },
"Kfz-Reparaturen (Gruppe)": {
"is_group": 1,
"Kfz-Reparaturen": { "Kfz-Reparaturen": {
"account_number": "6540" "account_number": "6540"
}
}, },
"Garagenmiete (Gruppe)": {
"is_group": 1,
"Garagenmiete": { "Garagenmiete": {
"account_number": "6550" "account_number": "6550"
}
}, },
"Mietleasing Kfz (Gruppe)": {
"is_group": 1,
"Mietleasing Kfz": { "Mietleasing Kfz": {
"account_number": "6560" "account_number": "6560"
}
}, },
"Sonstige Kfz-Kosten (Gruppe)": {
"is_group": 1,
"Sonstige Kfz-Kosten": { "Sonstige Kfz-Kosten": {
"account_number": "6570" "account_number": "6570"
}
}, },
"Mautgeb\u00fchren (Gruppe)": {
"is_group": 1,
"Mautgeb\u00fchren": { "Mautgeb\u00fchren": {
"account_number": "6580" "account_number": "6580"
}
}, },
"Kfz-Kosten f. betrieblich genutzte zum Privatverm\u00f6gen geh\u00f6rende Kraftfahrzeuge": { "Kfz-Kosten f. betrieblich genutzte zum Privatverm\u00f6gen geh\u00f6rende Kraftfahrzeuge": {
"account_number": "6590" "account_number": "6590"
@@ -3022,6 +2986,8 @@
"Nicht abzugsf\u00e4hige Betriebsausgaben aus Werbe- und Repr\u00e4sentationskosten": { "Nicht abzugsf\u00e4hige Betriebsausgaben aus Werbe- und Repr\u00e4sentationskosten": {
"account_number": "6645" "account_number": "6645"
}, },
"Reisekosten Arbeitnehmer (Gruppe)": {
"is_group": 1,
"Reisekosten Arbeitnehmer": { "Reisekosten Arbeitnehmer": {
"account_number": "6650" "account_number": "6650"
}, },
@@ -3036,6 +3002,7 @@
}, },
"Kilometergelderstattung Arbeitnehmer": { "Kilometergelderstattung Arbeitnehmer": {
"account_number": "6668" "account_number": "6668"
}
}, },
"Reisekosten Unternehmer (Gruppe)": { "Reisekosten Unternehmer (Gruppe)": {
"is_group": 1, "is_group": 1,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -281,8 +281,8 @@ def validate_party_gle_currency(party_type, party, company, party_account_curren
existing_gle_currency = get_party_gle_currency(party_type, party, company) existing_gle_currency = get_party_gle_currency(party_type, party, company)
if existing_gle_currency and party_account_currency != existing_gle_currency: if existing_gle_currency and party_account_currency != existing_gle_currency:
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}") frappe.throw(_("{0} {1} has accounting entries in currency {2} for company {3}. Please select a receivable or payable account with currency {2}.")
.format(party_type, party, existing_gle_currency), InvalidAccountCurrency) .format(frappe.bold(party_type), frappe.bold(party), frappe.bold(existing_gle_currency), frappe.bold(company)), InvalidAccountCurrency)
def validate_party_accounts(doc): def validate_party_accounts(doc):
companies = [] companies = []
@@ -295,15 +295,13 @@ def validate_party_accounts(doc):
companies.append(account.company) companies.append(account.company)
party_account_currency = frappe.db.get_value("Account", account.account, "account_currency", cache=True) party_account_currency = frappe.db.get_value("Account", account.account, "account_currency", cache=True)
existing_gle_currency = get_party_gle_currency(doc.doctype, doc.name, account.company)
if frappe.db.get_default("Company"): if frappe.db.get_default("Company"):
company_default_currency = frappe.get_cached_value('Company', company_default_currency = frappe.get_cached_value('Company',
frappe.db.get_default("Company"), "default_currency") frappe.db.get_default("Company"), "default_currency")
else: else:
company_default_currency = frappe.db.get_value('Company', account.company, "default_currency") company_default_currency = frappe.db.get_value('Company', account.company, "default_currency")
if existing_gle_currency and party_account_currency != existing_gle_currency: validate_party_gle_currency(doc.doctype, doc.name, account.company, party_account_currency)
frappe.throw(_("Accounting entries have already been made in currency {0} for company {1}. Please select a receivable or payable account with currency {0}.").format(existing_gle_currency, account.company))
if doc.get("default_currency") and party_account_currency and company_default_currency: if doc.get("default_currency") and party_account_currency and company_default_currency:
if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency: if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency:

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

@@ -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}
@@ -362,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

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
import erpnext
from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.desk.reportview import get_match_cond, get_filters_cond
from frappe.utils import nowdate, getdate from frappe.utils import nowdate, getdate
from collections import defaultdict from collections import defaultdict
@@ -129,23 +130,26 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
}) })
def tax_account_query(doctype, txt, searchfield, start, page_len, filters): def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
company_currency = erpnext.get_company_currency(filters.get('company'))
tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount
where tabAccount.docstatus!=2 where tabAccount.docstatus!=2
and account_type in (%s) and account_type in (%s)
and is_group = 0 and is_group = 0
and company = %s and company = %s
and account_currency = %s
and `%s` LIKE %s and `%s` LIKE %s
order by idx desc, name order by idx desc, name
limit %s, %s""" % limit %s, %s""" %
(", ".join(['%s']*len(filters.get("account_type"))), "%s", searchfield, "%s", "%s", "%s"), (", ".join(['%s']*len(filters.get("account_type"))), "%s", "%s", searchfield, "%s", "%s", "%s"),
tuple(filters.get("account_type") + [filters.get("company"), "%%%s%%" % txt, tuple(filters.get("account_type") + [filters.get("company"), company_currency, "%%%s%%" % txt,
start, page_len])) start, page_len]))
if not tax_accounts: if not tax_accounts:
tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount
where tabAccount.docstatus!=2 and is_group = 0 where tabAccount.docstatus!=2 and is_group = 0
and company = %s and `%s` LIKE %s limit %s, %s""" and company = %s and account_currency = %s and `%s` LIKE %s limit %s, %s""" #nosec
% ("%s", searchfield, "%s", "%s", "%s"), % ("%s", "%s", searchfield, "%s", "%s", "%s"),
(filters.get("company"), "%%%s%%" % txt, start, page_len)) (filters.get("company"), company_currency, "%%%s%%" % txt, start, page_len))
return tax_accounts return tax_accounts

View File

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

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
@@ -214,7 +215,8 @@
{ {
"fieldname": "opportunity_amount", "fieldname": "opportunity_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Opportunity Amount" "label": "Opportunity Amount",
"options": "currency"
}, },
{ {
"default": "0", "default": "0",
@@ -418,7 +420,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

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

View File

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

@@ -273,11 +273,11 @@ class TestLoan(unittest.TestCase):
penalty_amount = (accrued_interest_amount * 4 * 25) / (100 * days_in_year(get_datetime(first_date).year)) penalty_amount = (accrued_interest_amount * 4 * 25) / (100 * days_in_year(get_datetime(first_date).year))
lia = frappe.get_all("Loan Interest Accrual", fields=["is_paid"], lia1 = frappe.get_value("Loan Interest Accrual", {"loan": loan.name, "is_paid": 1}, 'name')
filters={"loan": loan.name}, order_by="posting_date") lia2 = frappe.get_value("Loan Interest Accrual", {"loan": loan.name, "is_paid": 0}, 'name')
self.assertTrue(lia[0].get('is_paid')) self.assertTrue(lia1)
self.assertFalse(lia[1].get('is_paid')) self.assertTrue(lia2)
def test_security_shortfall(self): def test_security_shortfall(self):
pledges = [] pledges = []

View File

@@ -43,8 +43,7 @@ frappe.ui.form.on("BOM", {
frm.set_query("item_code", "items", function() { frm.set_query("item_code", "items", function() {
return { return {
query: "erpnext.controllers.queries.item_query", query: "erpnext.controllers.queries.item_query"
filters: [["Item", "name", "!=", cur_frm.doc.item]]
}; };
}); });
@@ -135,6 +134,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

@@ -114,10 +114,6 @@ class BOM(WebsiteGenerator):
child = self.append('operations', d) child = self.append('operations', d)
child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2)
def validate_rm_item(self, item):
if (item[0]['name'] in [it.item_code for it in self.items]) and item[0]['name'] == self.item:
frappe.throw(_("BOM #{0}: Raw material cannot be same as main Item").format(self.name))
def set_bom_material_details(self): def set_bom_material_details(self):
for item in self.get("items"): for item in self.get("items"):
self.validate_bom_currecny(item) self.validate_bom_currecny(item)
@@ -147,7 +143,6 @@ class BOM(WebsiteGenerator):
args = json.loads(args) args = json.loads(args)
item = self.get_item_det(args['item_code']) item = self.get_item_det(args['item_code'])
self.validate_rm_item(item)
args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or '' args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or ''
args['transfer_for_manufacture'] = (cstr(args.get('include_item_in_manufacturing', '')) or args['transfer_for_manufacture'] = (cstr(args.get('include_item_in_manufacturing', '')) or

View File

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

View File

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

View File

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

View File

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

View File

@@ -314,7 +314,7 @@ class WorkOrder(Document):
stock_entry = frappe.db.sql("""select name from `tabStock Entry` stock_entry = frappe.db.sql("""select name from `tabStock Entry`
where work_order = %s and docstatus = 1""", self.name) where work_order = %s and docstatus = 1""", self.name)
if stock_entry: if stock_entry:
frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(stock_entry[0][0])) frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(frappe.utils.get_link_to_form('Stock Entry', stock_entry[0][0])))
def update_planned_qty(self): def update_planned_qty(self):
update_bin_qty(self.production_item, self.fg_warehouse, { update_bin_qty(self.production_item, self.fg_warehouse, {
@@ -552,24 +552,33 @@ class WorkOrder(Document):
d.db_set('transferred_qty', flt(transferred_qty), update_modified = False) d.db_set('transferred_qty', flt(transferred_qty), update_modified = False)
def update_consumed_qty_for_required_items(self): def update_consumed_qty_for_required_items(self):
'''update consumed qty from submitted stock entries for that item against '''
the work order''' Update consumed qty from submitted stock entries
against a work order for each stock item
'''
for d in self.required_items: for item in self.required_items:
consumed_qty = frappe.db.sql('''select sum(qty) consumed_qty = frappe.db.sql('''
from `tabStock Entry` entry, `tabStock Entry Detail` detail SELECT
where SUM(qty)
FROM
`tabStock Entry` entry,
`tabStock Entry Detail` detail
WHERE
entry.work_order = %(name)s entry.work_order = %(name)s
and (entry.purpose = "Material Consumption for Manufacture" AND (entry.purpose = "Material Consumption for Manufacture"
or entry.purpose = "Manufacture") OR entry.purpose = "Manufacture")
and entry.docstatus = 1 AND entry.docstatus = 1
and detail.parent = entry.name AND detail.parent = entry.name
and (detail.item_code = %(item)s or detail.original_item = %(item)s)''', { AND detail.s_warehouse IS NOT null
AND (detail.item_code = %(item)s
OR detail.original_item = %(item)s)
''', {
'name': self.name, 'name': self.name,
'item': d.item_code 'item': item.item_code
})[0][0] })[0][0]
d.db_set('consumed_qty', flt(consumed_qty), update_modified = False) item.db_set('consumed_qty', flt(consumed_qty), update_modified=False)
def make_bom(self): def make_bom(self):
data = frappe.db.sql(""" select sed.item_code, sed.qty, sed.s_warehouse data = frappe.db.sql(""" select sed.item_code, sed.qty, sed.s_warehouse
@@ -648,7 +657,7 @@ def get_item_details(item, project = None):
return res return res
@frappe.whitelist() @frappe.whitelist()
def make_work_order(item, qty=0, project=None): def make_work_order(bom_no, item, qty=0, project=None):
if not frappe.has_permission("Work Order", "write"): if not frappe.has_permission("Work Order", "write"):
frappe.throw(_("Not permitted"), frappe.PermissionError) frappe.throw(_("Not permitted"), frappe.PermissionError)
@@ -657,6 +666,7 @@ def make_work_order(item, qty=0, project=None):
wo_doc = frappe.new_doc("Work Order") wo_doc = frappe.new_doc("Work Order")
wo_doc.production_item = item wo_doc.production_item = item
wo_doc.update(item_details) wo_doc.update(item_details)
wo_doc.bom_no = bom_no
if flt(qty) > 0: if flt(qty) > 0:
wo_doc.qty = flt(qty) wo_doc.qty = flt(qty)

View File

@@ -658,4 +658,5 @@ erpnext.patches.v12_0.set_permission_einvoicing
erpnext.patches.v12_0.set_published_in_hub_tracked_item erpnext.patches.v12_0.set_published_in_hub_tracked_item
erpnext.patches.v12_0.set_job_offer_applicant_email erpnext.patches.v12_0.set_job_offer_applicant_email
erpnext.patches.v12_0.create_irs_1099_field_united_states erpnext.patches.v12_0.create_irs_1099_field_united_states
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22

View File

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

View File

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

View File

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

View File

@@ -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)
&& default_dimensions[frm.doc.company]) {
frm.trigger('update_dimension');
}
},
update_dimension: function(frm) {
erpnext.dimension_filters.forEach((dimension) => { erpnext.dimension_filters.forEach((dimension) => {
if (frm.is_new()) {
if (frm.doc.company && Object.keys(default_dimensions || {}).length > 0
&& default_dimensions[frm.doc.company]) {
if (frappe.meta.has_field(doctype, dimension['fieldname'])) { if (frappe.meta.has_field(doctype, dimension['fieldname'])) {
frm.set_value(dimension['fieldname'], erpnext.default_dimensions[frm.doc.company][dimension['document_type']]); frm.set_value(dimension['fieldname'],
default_dimensions[frm.doc.company][dimension['document_type']]);
}
$.each(frm.doc.items || frm.doc.accounts || [], function(i, row) {
frappe.model.set_value(row.doctype, row.name, dimension['fieldname'],
default_dimensions[frm.doc.company][dimension['document_type']])
});
}
} }
}); });
} }
},
}); });
}); });
erpnext.child_docs.forEach((doctype) => { child_docs.forEach((doctype) => {
frappe.ui.form.on(doctype, { frappe.ui.form.on(doctype, {
items_add: function(frm, cdt, cdn) { items_add: function(frm, cdt, cdn) {
erpnext.dimension_filters.forEach((dimension) => { erpnext.dimension_filters.forEach((dimension) => {
@@ -77,14 +78,6 @@ erpnext.child_docs.forEach((doctype) => {
}); });
}, },
company: function(frm) {
if(frm.doc.company) {
erpnext.dimension_filters.forEach((dimension) => {
frm.set_value(dimension['fieldname'], erpnext.default_dimensions[frm.doc.company][dimension['document_type']]);
});
}
},
items_add: function(frm, cdt, cdn) { items_add: function(frm, cdt, cdn) {
erpnext.dimension_filters.forEach((dimension) => { erpnext.dimension_filters.forEach((dimension) => {
var row = frappe.get_doc(cdt, cdn); var row = frappe.get_doc(cdt, cdn);

View File

@@ -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
}, },
@@ -444,7 +488,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

@@ -21,6 +21,12 @@ frappe.query_reports["DATEV"] = {
"default": frappe.datetime.now_date(), "default": frappe.datetime.now_date(),
"fieldtype": "Date", "fieldtype": "Date",
"reqd": 1 "reqd": 1
},
{
"fieldname": "voucher_type",
"label": __("Voucher Type"),
"fieldtype": "Select",
"options": "\nSales Invoice\nPurchase Invoice\nPayment Entry\nExpense Claim\nPayroll Entry\nBank Reconciliation\nAsset\nStock Entry"
} }
], ],
onload: function(query_report) { onload: function(query_report) {

View File

@@ -62,6 +62,7 @@ def get_transactions(filters, as_dict=1):
filters -- dict of filters to be passed to the sql query filters -- dict of filters to be passed to the sql query
as_dict -- return as list of dicts [0,1] as_dict -- return as list of dicts [0,1]
""" """
filter_by_voucher = 'AND gl.voucher_type = %(voucher_type)s' if filters.get('voucher_type') else ''
gl_entries = frappe.db.sql(""" gl_entries = frappe.db.sql("""
SELECT SELECT
@@ -80,8 +81,10 @@ def get_transactions(filters, as_dict=1):
gl.posting_date as 'Belegdatum', gl.posting_date as 'Belegdatum',
gl.voucher_no as 'Belegfeld 1', gl.voucher_no as 'Belegfeld 1',
gl.remarks as 'Buchungstext', gl.remarks as 'Buchungstext',
gl.against_voucher_type as 'Beleginfo - Art 1', gl.voucher_type as 'Beleginfo - Art 1',
gl.against_voucher as 'Beleginfo - Inhalt 1' gl.voucher_no as 'Beleginfo - Inhalt 1',
gl.against_voucher_type as 'Beleginfo - Art 2',
gl.against_voucher as 'Beleginfo - Inhalt 2'
FROM `tabGL Entry` gl FROM `tabGL Entry` gl
@@ -109,7 +112,8 @@ def get_transactions(filters, as_dict=1):
WHERE gl.company = %(company)s WHERE gl.company = %(company)s
AND DATE(gl.posting_date) >= %(from_date)s AND DATE(gl.posting_date) >= %(from_date)s
AND DATE(gl.posting_date) <= %(to_date)s AND DATE(gl.posting_date) <= %(to_date)s
ORDER BY 'Belegdatum', gl.voucher_no""", filters, as_dict=as_dict, as_utf8=1) {}
ORDER BY 'Belegdatum', gl.voucher_no""".format(filter_by_voucher), filters, as_dict=as_dict)
return gl_entries return gl_entries
@@ -160,7 +164,7 @@ def get_customers(filters):
and ccl.company = par.company and ccl.company = par.company
WHERE par.company = %(company)s WHERE par.company = %(company)s
AND par.parenttype = 'Customer'""", filters, as_dict=1, as_utf8=1) AND par.parenttype = 'Customer'""", filters, as_dict=1)
def get_suppliers(filters): def get_suppliers(filters):
@@ -217,7 +221,7 @@ def get_suppliers(filters):
and con.is_primary_contact = '1' and con.is_primary_contact = '1'
WHERE par.company = %(company)s WHERE par.company = %(company)s
AND par.parenttype = 'Supplier'""", filters, as_dict=1, as_utf8=1) AND par.parenttype = 'Supplier'""", filters, as_dict=1)
def get_account_names(filters): def get_account_names(filters):
@@ -281,7 +285,7 @@ def get_datev_csv(data, filters, csv_class):
def get_header(filters, csv_class): def get_header(filters, csv_class):
coa = frappe.get_value("Company", filters.get("company"), "chart_of_accounts") coa = frappe.get_value("Company", filters.get("company"), "chart_of_accounts")
coa_used = "SKR04" if "SKR04" in coa else ("SKR03" if "SKR03" in coa else "") coa_used = "04" if "SKR04" in coa else ("03" if "SKR03" in coa else "")
header = [ header = [
# DATEV format # DATEV format
@@ -298,7 +302,7 @@ def get_header(filters, csv_class):
# Format version (regarding format name) # Format version (regarding format name)
csv_class.FORMAT_VERSION, csv_class.FORMAT_VERSION,
# Generated on # Generated on
datetime.datetime.now().strftime("%Y%m%d%H%M%S"), datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '000',
# Imported on -- stays empty # Imported on -- stays empty
'', '',
# Origin. Any two symbols, will be replaced by "SV" on import. # Origin. Any two symbols, will be replaced by "SV" on import.
@@ -332,9 +336,17 @@ def get_header(filters, csv_class):
# 2 = Annual financial statement (Jahresabschluss) # 2 = Annual financial statement (Jahresabschluss)
'1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', '1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
# T = Rechnungslegungszweck # T = Rechnungslegungszweck
'', # 0 oder leer = vom Rechnungslegungszweck unabhängig
# 50 = Handelsrecht
# 30 = Steuerrecht
# 64 = IFRS
# 40 = Kalkulatorik
# 11 = Reserviert
# 12 = Reserviert
'0',
# U = Festschreibung # U = Festschreibung
'', # TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1"
'0',
# V = Default currency, for example, "EUR" # V = Default currency, for example, "EUR"
'"%s"' % frappe.get_value("Company", filters.get("company"), "default_currency"), '"%s"' % frappe.get_value("Company", filters.get("company"), "default_currency"),
# reserviert # reserviert

View File

@@ -498,13 +498,27 @@ QUERY_REPORT_COLUMNS = [
}, },
{ {
"label": "Beleginfo - Art 1", "label": "Beleginfo - Art 1",
"fieldname": "Beleginfo - Art 2", "fieldname": "Beleginfo - Art 1",
"fieldtype": "Data", "fieldtype": "Link",
"options": "DocType"
}, },
{ {
"label": "Beleginfo - Inhalt 1", "label": "Beleginfo - Inhalt 1",
"fieldname": "Beleginfo - Inhalt 1",
"fieldtype": "Dynamic Link",
"options": "Beleginfo - Art 1"
},
{
"label": "Beleginfo - Art 2",
"fieldname": "Beleginfo - Art 2",
"fieldtype": "Link",
"options": "DocType"
},
{
"label": "Beleginfo - Inhalt 2",
"fieldname": "Beleginfo - Inhalt 2", "fieldname": "Beleginfo - Inhalt 2",
"fieldtype": "Data", "fieldtype": "Dynamic Link",
"options": "Beleginfo - Art 2"
} }
] ]

View File

@@ -54,8 +54,8 @@ class Gstr1Report(object):
return self.columns, self.data return self.columns, self.data
def get_data(self): def get_data(self):
if self.filters.get("type_of_business") == "B2C Small": if self.filters.get("type_of_business") in ("B2C Small", "B2C Large"):
self.get_b2cs_data() self.get_b2c_data()
else: else:
for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
invoice_details = self.invoices.get(inv) invoice_details = self.invoices.get(inv)
@@ -69,7 +69,7 @@ class Gstr1Report(object):
if taxable_value: if taxable_value:
self.data.append(row) self.data.append(row)
def get_b2cs_data(self): def get_b2c_data(self):
b2cs_output = {} b2cs_output = {}
for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
@@ -84,7 +84,10 @@ class Gstr1Report(object):
"rate": "", "rate": "",
"taxable_value": 0, "taxable_value": 0,
"cess_amount": 0, "cess_amount": 0,
"type": "" "type": "",
"invoice_number": invoice_details.get("invoice_number"),
"posting_date": invoice_details.get("posting_date"),
"invoice_value": invoice_details.get("base_grand_total"),
}) })
row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin)) row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin))
@@ -164,7 +167,7 @@ class Gstr1Report(object):
frappe.throw(_("Please set B2C Limit in GST Settings.")) frappe.throw(_("Please set B2C Limit in GST Settings."))
if self.filters.get("type_of_business") == "B2C Large": if self.filters.get("type_of_business") == "B2C Large":
conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2) conditions += """ and ifnull(SUBSTR(place_of_supply, 1, 2),'') != ifnull(SUBSTR(company_gstin, 1, 2),'')
and grand_total > {0} and is_return != 1 and gst_category ='Unregistered' """.format(flt(b2c_limit)) and grand_total > {0} and is_return != 1 and gst_category ='Unregistered' """.format(flt(b2c_limit))
elif self.filters.get("type_of_business") == "B2C Small": elif self.filters.get("type_of_business") == "B2C Small":
@@ -581,6 +584,11 @@ def get_b2b_json(res, gstin):
if not gst_in: continue if not gst_in: continue
for number, invoice in iteritems(res[gst_in]): for number, invoice in iteritems(res[gst_in]):
if not invoice[0]["place_of_supply"]:
frappe.throw(_("""{0} not entered in Invoice {1}.
Please update and try again""").format(frappe.bold("Place Of Supply"),
frappe.bold(invoice[0]['invoice_number'])))
inv_item = get_basic_invoice_detail(invoice[0]) inv_item = get_basic_invoice_detail(invoice[0])
inv_item["pos"] = "%02d" % int(invoice[0]["place_of_supply"].split('-')[0]) inv_item["pos"] = "%02d" % int(invoice[0]["place_of_supply"].split('-')[0])
inv_item["rchrg"] = invoice[0]["reverse_charge"] inv_item["rchrg"] = invoice[0]["reverse_charge"]
@@ -606,6 +614,9 @@ def get_b2cs_json(data, gstin):
out = [] out = []
for d in data: for d in data:
if not d.get("place_of_supply"):
frappe.throw(_("""{0} not entered in some invoices.
Please update and try again""").format(frappe.bold("Place Of Supply")))
pos = d.get('place_of_supply').split('-')[0] pos = d.get('place_of_supply').split('-')[0]
tax_details = {} tax_details = {}
@@ -642,6 +653,10 @@ def get_b2cs_json(data, gstin):
def get_b2cl_json(res, gstin): def get_b2cl_json(res, gstin):
out = [] out = []
for pos in res: for pos in res:
if not pos:
frappe.throw(_("""{0} not entered in some invoices.
Please update and try again""").format(frappe.bold("Place Of Supply")))
b2cl_item, inv = {"pos": "%02d" % int(pos.split('-')[0]), "inv": []}, [] b2cl_item, inv = {"pos": "%02d" % int(pos.split('-')[0]), "inv": []}, []
for row in res[pos]: for row in res[pos]:

View File

@@ -268,9 +268,11 @@ def make_quotation(source_name, target_doc=None):
target_doc.run_method("set_other_charges") target_doc.run_method("set_other_charges")
target_doc.run_method("calculate_taxes_and_totals") target_doc.run_method("calculate_taxes_and_totals")
price_list = frappe.get_value("Customer", source_name, 'default_price_list') price_list, currency = frappe.db.get_value("Customer", {'name': source_name}, ['default_price_list', 'default_currency'])
if price_list: if price_list:
target_doc.selling_price_list = price_list target_doc.selling_price_list = price_list
if currency:
target_doc.currency = currency
return target_doc return target_doc

View File

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

View File

@@ -496,7 +496,7 @@ def close_or_unclose_sales_orders(names, status):
def get_requested_item_qty(sales_order): def get_requested_item_qty(sales_order):
return frappe._dict(frappe.db.sql(""" return frappe._dict(frappe.db.sql("""
select sales_order_item, sum(stock_qty) select sales_order_item, sum(qty)
from `tabMaterial Request Item` from `tabMaterial Request Item`
where docstatus = 1 where docstatus = 1
and sales_order = %s and sales_order = %s
@@ -507,16 +507,12 @@ def get_requested_item_qty(sales_order):
def make_material_request(source_name, target_doc=None): def make_material_request(source_name, target_doc=None):
requested_item_qty = get_requested_item_qty(source_name) requested_item_qty = get_requested_item_qty(source_name)
def postprocess(source, doc):
doc.material_request_type = "Purchase"
def update_item(source, target, source_parent): def update_item(source, target, source_parent):
# qty is for packed items, because packed items don't have stock_qty field # qty is for packed items, because packed items don't have stock_qty field
qty = source.get("stock_qty") or source.get("qty") qty = source.get("qty")
target.project = source_parent.project target.project = source_parent.project
target.qty = qty - requested_item_qty.get(source.name, 0) target.qty = qty - requested_item_qty.get(source.name, 0)
target.conversion_factor = 1 target.stock_qty = flt(target.qty) * flt(target.conversion_factor)
target.stock_qty = qty - requested_item_qty.get(source.name, 0)
doc = get_mapped_doc("Sales Order", source_name, { doc = get_mapped_doc("Sales Order", source_name, {
"Sales Order": { "Sales Order": {
@@ -537,14 +533,12 @@ def make_material_request(source_name, target_doc=None):
"doctype": "Material Request Item", "doctype": "Material Request Item",
"field_map": { "field_map": {
"name": "sales_order_item", "name": "sales_order_item",
"parent": "sales_order", "parent": "sales_order"
"stock_uom": "uom",
"stock_qty": "qty"
}, },
"condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code) and doc.stock_qty > requested_item_qty.get(doc.name, 0), "condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code) and doc.stock_qty > requested_item_qty.get(doc.name, 0),
"postprocess": update_item "postprocess": update_item
} }
}, target_doc, postprocess) }, target_doc)
return doc return doc

View File

@@ -121,8 +121,8 @@ def get_columns(filters):
}, },
{ {
"label": _("Billed Amount"), "label": _("Billed Amount"),
"fieldname": "rate", "fieldtype": "currency",
"options": "billed_amount", "fieldname": "billed_amount",
"width": 120 "width": 120
}, },
{ {

View File

@@ -413,15 +413,20 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
*/ */
set_batch_number: function(cdt, cdn) { set_batch_number: function(cdt, cdn) {
const doc = frappe.get_doc(cdt, cdn); const doc = frappe.get_doc(cdt, cdn);
if (doc && doc.has_batch_no) { if (doc && doc.has_batch_no && doc.warehouse) {
this._set_batch_number(doc); this._set_batch_number(doc);
} }
}, },
_set_batch_number: function(doc) { _set_batch_number: function(doc) {
let args = {'item_code': doc.item_code, 'warehouse': doc.warehouse, 'qty': flt(doc.qty) * flt(doc.conversion_factor)};
if (doc.has_serial_no && doc.serial_no) {
args['serial_no'] = doc.serial_no
}
return frappe.call({ return frappe.call({
method: 'erpnext.stock.doctype.batch.batch.get_batch_no', method: 'erpnext.stock.doctype.batch.batch.get_batch_no',
args: {'item_code': doc.item_code, 'warehouse': doc.warehouse, 'qty': flt(doc.qty) * flt(doc.conversion_factor)}, args: args,
callback: function(r) { callback: function(r) {
if(r.message) { if(r.message) {
frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message); frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message);

View File

@@ -41,8 +41,7 @@
"charts": [ "charts": [
{ {
"chart_name": "Bank Balance", "chart_name": "Bank Balance",
"label": "All Your Money", "label": "Bank Balance"
"size": "Full"
} }
], ],
"creation": "2020-01-23 13:46:38.833076", "creation": "2020-01-23 13:46:38.833076",
@@ -55,7 +54,7 @@
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "Getting Started", "label": "Getting Started",
"modified": "2020-03-12 16:30:37.821762", "modified": "2020-03-23 11:20:49.161823",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Getting Started", "name": "Getting Started",
@@ -82,6 +81,16 @@
"is_query_report": 0, "is_query_report": 0,
"link_to": "Sales Invoice", "link_to": "Sales Invoice",
"type": "DocType" "type": "DocType"
},
{
"is_query_report": 0,
"link_to": "dashboard",
"type": "Page"
},
{
"is_query_report": 0,
"link_to": "leaderboard",
"type": "Page"
} }
] ]
} }

View File

@@ -157,6 +157,7 @@
{ {
"fieldname": "parent_company", "fieldname": "parent_company",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Parent Company", "label": "Parent Company",
"options": "Company" "options": "Company"
@@ -277,6 +278,7 @@
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"",
"fieldname": "existing_company", "fieldname": "existing_company",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Existing Company ", "label": "Existing Company ",
"no_copy": 1, "no_copy": 1,
"options": "Company" "options": "Company"
@@ -728,7 +730,7 @@
"image_field": "company_logo", "image_field": "company_logo",
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2020-03-18 18:09:53.534211", "modified": "2020-03-21 18:09:53.534211",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Company", "name": "Company",

View File

@@ -13,12 +13,16 @@ install_docs = [
] ]
def get_warehouse_account_map(company=None): def get_warehouse_account_map(company=None):
if not frappe.flags.warehouse_account_map or frappe.flags.in_test: company_warehouse_account_map = company and frappe.flags.setdefault('warehouse_account_map', {}).get(company)
warehouse_account_map = frappe.flags.warehouse_account_map
if not warehouse_account_map or not company_warehouse_account_map or frappe.flags.in_test:
warehouse_account = frappe._dict() warehouse_account = frappe._dict()
filters = {} filters = {}
if company: if company:
filters['company'] = company filters['company'] = company
frappe.flags.setdefault('warehouse_account_map', {}).setdefault(company, {})
for d in frappe.get_all('Warehouse', for d in frappe.get_all('Warehouse',
fields = ["name", "account", "parent_warehouse", "company", "is_group"], fields = ["name", "account", "parent_warehouse", "company", "is_group"],
@@ -30,10 +34,12 @@ def get_warehouse_account_map(company=None):
if d.account: if d.account:
d.account_currency = frappe.db.get_value('Account', d.account, 'account_currency', cache=True) d.account_currency = frappe.db.get_value('Account', d.account, 'account_currency', cache=True)
warehouse_account.setdefault(d.name, d) warehouse_account.setdefault(d.name, d)
if company:
frappe.flags.warehouse_account_map[company] = warehouse_account
else:
frappe.flags.warehouse_account_map = warehouse_account frappe.flags.warehouse_account_map = warehouse_account
return frappe.flags.warehouse_account_map return frappe.flags.warehouse_account_map.get(company) or frappe.flags.warehouse_account_map
def get_warehouse_account(warehouse, warehouse_account=None): def get_warehouse_account(warehouse, warehouse_account=None):
account = warehouse.account account = warehouse.account

View File

@@ -122,8 +122,11 @@ class Batch(Document):
self.expiry_date = add_days(self.manufacturing_date, shelf_life_in_days) self.expiry_date = add_days(self.manufacturing_date, shelf_life_in_days)
if has_expiry_date and not self.expiry_date: if has_expiry_date and not self.expiry_date:
frappe.msgprint(_('Expiry date is mandatory for selected item.')) frappe.throw(msg=_("Please set {0} for Batched Item {1}, which is used to set {2} on Submit.") \
frappe.throw(_("Set item's shelf life in days, to set expiry based on manufacturing date plus shelf-life.")) .format(frappe.bold("Shelf Life in Days"),
frappe.utils.get_link_to_form("Item", self.item),
frappe.bold("Batch Expiry Date")),
title=_("Expiry Date Mandatory"))
def get_name_from_naming_series(self): def get_name_from_naming_series(self):
""" """

View File

@@ -238,7 +238,7 @@ class DeliveryTrip(Document):
try: try:
directions = maps_client.directions(**directions_data) directions = maps_client.directions(**directions_data)
except Exception as e: except Exception as e:
frappe.throw(_(e)) frappe.throw(_(str(e)))
return directions[0] if directions else False return directions[0] if directions else False

View File

@@ -343,7 +343,8 @@
{ {
"fieldname": "shelf_life_in_days", "fieldname": "shelf_life_in_days",
"fieldtype": "Int", "fieldtype": "Int",
"label": "Shelf Life In Days" "label": "Shelf Life In Days",
"mandatory_depends_on": "eval:doc.has_batch_no && doc.has_expiry_date"
}, },
{ {
"default": "2099-12-31", "default": "2099-12-31",
@@ -1045,7 +1046,7 @@
"image_field": "image", "image_field": "image",
"links": [], "links": [],
"max_attachments": 1, "max_attachments": 1,
"modified": "2020-01-02 19:13:59.295963", "modified": "2020-03-24 16:14:36.950677",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Item", "name": "Item",

View File

@@ -741,13 +741,11 @@ class Item(WebsiteGenerator):
defaults = frappe.defaults.get_defaults() or {} defaults = frappe.defaults.get_defaults() or {}
# To check default warehouse is belong to the default company # To check default warehouse is belong to the default company
if defaults.get("default_warehouse") and frappe.db.exists("Warehouse", if defaults.get("default_warehouse") and defaults.company and frappe.db.exists("Warehouse",
{'name': defaults.default_warehouse, 'company': defaults.company}): {'name': defaults.default_warehouse, 'company': defaults.company}):
warehouse = defaults.default_warehouse
self.append("item_defaults", { self.append("item_defaults", {
"company": defaults.get("company"), "company": defaults.get("company"),
"default_warehouse": warehouse "default_warehouse": defaults.default_warehouse
}) })
def update_variants(self): def update_variants(self):

View File

@@ -8,6 +8,7 @@ from frappe.utils import flt
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.accounts.doctype.account.account import get_account_currency
class LandedCostVoucher(Document): class LandedCostVoucher(Document):
def get_items_from_purchase_receipts(self): def get_items_from_purchase_receipts(self):
@@ -43,6 +44,7 @@ class LandedCostVoucher(Document):
else: else:
self.validate_applicable_charges_for_item() self.validate_applicable_charges_for_item()
self.validate_purchase_receipts() self.validate_purchase_receipts()
self.validate_expense_accounts()
self.set_total_taxes_and_charges() self.set_total_taxes_and_charges()
def check_mandatory(self): def check_mandatory(self):
@@ -71,6 +73,14 @@ class LandedCostVoucher(Document):
frappe.throw(_("Row {0}: Cost center is required for an item {1}") frappe.throw(_("Row {0}: Cost center is required for an item {1}")
.format(item.idx, item.item_code)) .format(item.idx, item.item_code))
def validate_expense_accounts(self):
company_currency = erpnext.get_company_currency(self.company)
for account in self.taxes:
if get_account_currency(account.expense_account) != company_currency:
frappe.throw(msg=_(""" Row {0}: Expense account currency should be same as company's default currency.
Please select expense account with account currency as {1}""")
.format(account.idx, frappe.bold(company_currency)), title=_("Invalid Account Currency"))
def set_total_taxes_and_charges(self): def set_total_taxes_and_charges(self):
self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")]) self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")])

View File

@@ -501,7 +501,7 @@ def raise_work_orders(material_request):
wo_order = frappe.new_doc("Work Order") wo_order = frappe.new_doc("Work Order")
wo_order.update({ wo_order.update({
"production_item": d.item_code, "production_item": d.item_code,
"qty": d.qty - d.ordered_qty, "qty": d.stock_qty - d.ordered_qty,
"fg_warehouse": d.warehouse, "fg_warehouse": d.warehouse,
"wip_warehouse": default_wip_warehouse, "wip_warehouse": default_wip_warehouse,
"description": d.description, "description": d.description,

View File

@@ -523,12 +523,15 @@ def get_delivery_note_serial_no(item_code, qty, delivery_note):
return serial_nos return serial_nos
@frappe.whitelist() @frappe.whitelist()
def auto_fetch_serial_number(qty, item_code, warehouse, batch_no=None): def auto_fetch_serial_number(qty, item_code, warehouse, batch_nos=None):
serial_numbers = frappe.get_list("Serial No", filters={ import json
filters = {
"item_code": item_code, "item_code": item_code,
"warehouse": warehouse, "warehouse": warehouse,
"batch_no": batch_no,
"delivery_document_no": "", "delivery_document_no": "",
"sales_invoice": "" "sales_invoice": ""
}, limit=qty, order_by="creation") }
if batch_nos: filters["batch_no"] = ["in", json.loads(batch_nos)]
serial_numbers = frappe.get_list("Serial No", filters=filters, limit=qty, order_by="creation")
return [item['name'] for item in serial_numbers] return [item['name'] for item in serial_numbers]

View File

@@ -60,7 +60,8 @@ frappe.ui.form.on('Stock Entry', {
} }
} }
if(item.s_warehouse) filters["warehouse"] = item.s_warehouse; filters["warehouse"] = item.s_warehouse || item.t_warehouse;
return { return {
query : "erpnext.controllers.queries.get_batch_no", query : "erpnext.controllers.queries.get_batch_no",
filters: filters filters: filters
@@ -309,14 +310,14 @@ frappe.ui.form.on('Stock Entry', {
method: "erpnext.stock.get_item_details.get_serial_no", method: "erpnext.stock.get_item_details.get_serial_no",
args: {"args": args}, args: {"args": args},
callback: function(r) { callback: function(r) {
if (!r.exe){ if (!r.exe && r.message){
frappe.model.set_value(cdt, cdn, "serial_no", r.message); frappe.model.set_value(cdt, cdn, "serial_no", r.message);
}
if (callback) { if (callback) {
callback(); callback();
} }
} }
}
}); });
}, },
@@ -622,12 +623,17 @@ frappe.ui.form.on('Stock Entry Detail', {
if(r.message) { if(r.message) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
$.each(r.message, function(k, v) { $.each(r.message, function(k, v) {
if (v) {
frappe.model.set_value(cdt, cdn, k, v); // qty and it's subsequent fields weren't triggered frappe.model.set_value(cdt, cdn, k, v); // qty and it's subsequent fields weren't triggered
}
}); });
refresh_field("items"); refresh_field("items");
if (!d.serial_no) {
erpnext.stock.select_batch_and_serial_no(frm, d); erpnext.stock.select_batch_and_serial_no(frm, d);
} }
} }
}
}); });
} }
}, },
@@ -964,7 +970,7 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => {
} }
} }
if(item && !item.has_serial_no && item.has_batch_no) return; if(item && !item.has_serial_no && !item.has_batch_no) return;
if (frm.doc.purpose === 'Material Receipt') return; if (frm.doc.purpose === 'Material Receipt') return;
frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() { frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() {

View File

@@ -177,6 +177,10 @@ class StockEntry(StockController):
stock_items = self.get_stock_items() stock_items = self.get_stock_items()
serialized_items = self.get_serialized_items() serialized_items = self.get_serialized_items()
for item in self.get("items"): for item in self.get("items"):
if item.qty and item.qty < 0:
frappe.throw(_("Row {0}: The item {1}, quantity must be positive number")
.format(item.idx, frappe.bold(item.item_code)))
if item.item_code not in stock_items: if item.item_code not in stock_items:
frappe.throw(_("{0} is not a stock Item").format(item.item_code)) frappe.throw(_("{0} is not a stock Item").format(item.item_code))
@@ -234,7 +238,7 @@ class StockEntry(StockController):
if self.purpose == "Manufacture" and self.work_order: if self.purpose == "Manufacture" and self.work_order:
production_item = frappe.get_value('Work Order', self.work_order, 'production_item') production_item = frappe.get_value('Work Order', self.work_order, 'production_item')
for item in self.items: for item in self.items:
if item.item_code == production_item and item.qty != self.fg_completed_qty: if item.item_code == production_item and item.t_warehouse and item.qty != self.fg_completed_qty:
frappe.throw(_("Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different") frappe.throw(_("Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different")
.format(item.qty, self.fg_completed_qty)) .format(item.qty, self.fg_completed_qty))
@@ -294,13 +298,8 @@ class StockEntry(StockController):
if validate_for_manufacture: if validate_for_manufacture:
if d.bom_no: if d.bom_no:
d.s_warehouse = None d.s_warehouse = None
if not d.t_warehouse: if not d.t_warehouse:
frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
elif self.pro_doc and (cstr(d.t_warehouse) != self.pro_doc.fg_warehouse and cstr(d.t_warehouse) != self.pro_doc.scrap_warehouse):
frappe.throw(_("Target warehouse in row {0} must be same as Work Order").format(d.idx))
else: else:
d.t_warehouse = None d.t_warehouse = None
if not d.s_warehouse: if not d.s_warehouse:

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "hash", "autoname": "hash",
"creation": "2013-03-29 18:22:12", "creation": "2013-03-29 18:22:12",
"doctype": "DocType", "doctype": "DocType",
@@ -479,8 +480,7 @@
"fieldname": "project", "fieldname": "project",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Project", "label": "Project",
"options": "Project", "options": "Project"
"read_only": 1
}, },
{ {
"fieldname": "po_detail", "fieldname": "po_detail",
@@ -494,7 +494,8 @@
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2019-08-20 14:01:02.319754", "links": [],
"modified": "2020-03-19 12:34:09.836295",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock Entry Detail", "name": "Stock Entry Detail",

View File

@@ -29,7 +29,13 @@ frappe.query_reports["Stock Ledger"] = {
"fieldname":"warehouse", "fieldname":"warehouse",
"label": __("Warehouse"), "label": __("Warehouse"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Warehouse" "options": "Warehouse",
"get_query": function() {
const company = frappe.query_report.get_filter_value('company');
return {
filters: { 'company': company }
}
}
}, },
{ {
"fieldname":"item_code", "fieldname":"item_code",

View File

@@ -18,7 +18,7 @@
"is_standard": 1, "is_standard": 1,
"login_required": 1, "login_required": 1,
"max_attachment_size": 0, "max_attachment_size": 0,
"modified": "2019-12-10 13:48:19.894186", "modified": "2020-03-06 05:24:05.749664",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Support", "module": "Support",
"name": "issues", "name": "issues",
@@ -58,7 +58,7 @@
"options": "Open\nReplied\nHold\nClosed", "options": "Open\nReplied\nHold\nClosed",
"read_only": 1, "read_only": 1,
"reqd": 0, "reqd": 0,
"show_in_filter": 0 "show_in_filter": 1
}, },
{ {
"allow_read_on_all_link_options": 0, "allow_read_on_all_link_options": 0,

View File

@@ -7,6 +7,8 @@ import frappe
def create_test_contact_and_address(): def create_test_contact_and_address():
frappe.db.sql('delete from tabContact') frappe.db.sql('delete from tabContact')
frappe.db.sql('delete from `tabContact Email`')
frappe.db.sql('delete from `tabContact Phone`')
frappe.db.sql('delete from tabAddress') frappe.db.sql('delete from tabAddress')
frappe.db.sql('delete from `tabDynamic Link`') frappe.db.sql('delete from `tabDynamic Link`')

View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
import frappe.share import frappe.share
from frappe import _ from frappe import _
from frappe.utils import cstr, now_datetime, cint, flt, get_time from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_link_to_form
from erpnext.controllers.status_updater import StatusUpdater from erpnext.controllers.status_updater import StatusUpdater
from six import string_types from six import string_types
@@ -123,8 +123,11 @@ class TransactionBase(StatusUpdater):
ref_rate = frappe.db.get_value(ref_dt + " Item", d.get(ref_link_field), "rate") ref_rate = frappe.db.get_value(ref_dt + " Item", d.get(ref_link_field), "rate")
if abs(flt(d.rate - ref_rate, d.precision("rate"))) >= .01: if abs(flt(d.rate - ref_rate, d.precision("rate"))) >= .01:
frappe.throw(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ") frappe.msgprint(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ")
.format(d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate)) .format(d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate))
frappe.throw(_("To allow different rates, disable the {0} checkbox in {1}.")
.format(frappe.bold("Maintain Same Rate Throughout Sales Cycle"),
get_link_to_form("Selling Settings", "Selling Settings", frappe.bold("Selling Settings"))))
def get_link_filters(self, for_doctype): def get_link_filters(self, for_doctype):
if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype): if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype):