Merge branch 'contacts-ref' of https://github.com/hrwx/erpnext into contacts-ref

This commit is contained in:
Himanshu Warekar 2019-08-26 12:49:45 +05:30
commit ba6fe6cde5
13 changed files with 120 additions and 77 deletions

View File

@ -8,7 +8,8 @@
"customer", "customer",
"column_break_3", "column_break_3",
"posting_date", "posting_date",
"outstanding_amount" "outstanding_amount",
"debit_to"
], ],
"fields": [ "fields": [
{ {
@ -48,10 +49,18 @@
{ {
"fieldname": "column_break_3", "fieldname": "column_break_3",
"fieldtype": "Column Break" "fieldtype": "Column Break"
},
{
"fetch_from": "sales_invoice.debit_to",
"fieldname": "debit_to",
"fieldtype": "Link",
"label": "Debit to",
"options": "Account",
"read_only": 1
} }
], ],
"istable": 1, "istable": 1,
"modified": "2019-05-30 19:27:29.436153", "modified": "2019-08-07 15:13:55.808349",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Discounted Invoice", "name": "Discounted Invoice",

View File

@ -13,41 +13,57 @@ frappe.ui.form.on('Invoice Discounting', {
}; };
}); });
frm.events.filter_accounts("bank_account", frm, {"account_type": "Bank"});
frm.events.filter_accounts("bank_charges_account", frm, {"root_type": "Expense"}); frm.events.filter_accounts("bank_account", frm, [["account_type", "=", "Bank"]]);
frm.events.filter_accounts("short_term_loan", frm, {"root_type": "Liability"}); frm.events.filter_accounts("bank_charges_account", frm, [["root_type", "=", "Expense"]]);
frm.events.filter_accounts("accounts_receivable_credit", frm, {"account_type": "Receivable"}); frm.events.filter_accounts("short_term_loan", frm, [["root_type", "=", "Liability"]]);
frm.events.filter_accounts("accounts_receivable_discounted", frm, {"account_type": "Receivable"}); frm.events.filter_accounts("accounts_receivable_discounted", frm, [["account_type", "=", "Receivable"]]);
frm.events.filter_accounts("accounts_receivable_unpaid", frm, {"account_type": "Receivable"}); frm.events.filter_accounts("accounts_receivable_credit", frm, [["account_type", "=", "Receivable"]]);
frm.events.filter_accounts("accounts_receivable_unpaid", frm, [["account_type", "=", "Receivable"]]);
}, },
filter_accounts: (fieldname, frm, addl_filters) => { filter_accounts: (fieldname, frm, addl_filters) => {
let filters = { let filters = [
"company": frm.doc.company, ["company", "=", frm.doc.company],
"is_group": 0 ["is_group", "=", 0]
}; ];
if(addl_filters) Object.assign(filters, addl_filters); if(addl_filters){
filters = $.merge(filters , addl_filters);
}
frm.set_query(fieldname, () => { return { "filters": filters }; }); frm.set_query(fieldname, () => { return { "filters": filters }; });
}, },
refresh_filters: (frm) =>{
let invoice_accounts = Object.keys(frm.doc.invoices).map(function(key) {
return frm.doc.invoices[key].debit_to;
});
let filters = [
["account_type", "=", "Receivable"],
["name", "not in", invoice_accounts]
];
frm.events.filter_accounts("accounts_receivable_credit", frm, filters);
frm.events.filter_accounts("accounts_receivable_discounted", frm, filters);
frm.events.filter_accounts("accounts_receivable_unpaid", frm, filters);
},
refresh: (frm) => { refresh: (frm) => {
frm.events.show_general_ledger(frm); frm.events.show_general_ledger(frm);
if(frm.doc.docstatus === 0) { if (frm.doc.docstatus === 0) {
frm.add_custom_button(__('Get Invoices'), function() { frm.add_custom_button(__('Get Invoices'), function() {
frm.events.get_invoices(frm); frm.events.get_invoices(frm);
}); });
} }
if(frm.doc.docstatus === 1 && frm.doc.status !== "Settled") { if (frm.doc.docstatus === 1 && frm.doc.status !== "Settled") {
if(frm.doc.status == "Sanctioned") { if (frm.doc.status == "Sanctioned") {
frm.add_custom_button(__('Disburse Loan'), function() { frm.add_custom_button(__('Disburse Loan'), function() {
frm.events.create_disbursement_entry(frm); frm.events.create_disbursement_entry(frm);
}).addClass("btn-primary"); }).addClass("btn-primary");
} }
if(frm.doc.status == "Disbursed") { if (frm.doc.status == "Disbursed") {
frm.add_custom_button(__('Close Loan'), function() { frm.add_custom_button(__('Close Loan'), function() {
frm.events.close_loan(frm); frm.events.close_loan(frm);
}).addClass("btn-primary"); }).addClass("btn-primary");
@ -64,7 +80,7 @@ frappe.ui.form.on('Invoice Discounting', {
}, },
set_end_date: (frm) => { set_end_date: (frm) => {
if(frm.doc.loan_start_date && frm.doc.loan_period) { if (frm.doc.loan_start_date && frm.doc.loan_period) {
let end_date = frappe.datetime.add_days(frm.doc.loan_start_date, frm.doc.loan_period); let end_date = frappe.datetime.add_days(frm.doc.loan_start_date, frm.doc.loan_period);
frm.set_value("loan_end_date", end_date); frm.set_value("loan_end_date", end_date);
} }
@ -132,6 +148,7 @@ frappe.ui.form.on('Invoice Discounting', {
frm.doc.invoices = frm.doc.invoices.filter(row => row.sales_invoice); frm.doc.invoices = frm.doc.invoices.filter(row => row.sales_invoice);
let row = frm.add_child("invoices"); let row = frm.add_child("invoices");
$.extend(row, v); $.extend(row, v);
frm.events.refresh_filters(frm);
}); });
refresh_field("invoices"); refresh_field("invoices");
} }
@ -190,8 +207,10 @@ frappe.ui.form.on('Invoice Discounting', {
frappe.ui.form.on('Discounted Invoice', { frappe.ui.form.on('Discounted Invoice', {
sales_invoice: (frm) => { sales_invoice: (frm) => {
frm.events.calculate_total_amount(frm); frm.events.calculate_total_amount(frm);
frm.events.refresh_filters(frm);
}, },
invoices_remove: (frm) => { invoices_remove: (frm) => {
frm.events.calculate_total_amount(frm); frm.events.calculate_total_amount(frm);
frm.events.refresh_filters(frm);
} }
}); });

View File

@ -12,6 +12,7 @@ from erpnext.accounts.general_ledger import make_gl_entries
class InvoiceDiscounting(AccountsController): class InvoiceDiscounting(AccountsController):
def validate(self): def validate(self):
self.validate_mandatory() self.validate_mandatory()
self.validate_invoices()
self.calculate_total_amount() self.calculate_total_amount()
self.set_status() self.set_status()
self.set_end_date() self.set_end_date()
@ -24,6 +25,15 @@ class InvoiceDiscounting(AccountsController):
if self.docstatus == 1 and not (self.loan_start_date and self.loan_period): if self.docstatus == 1 and not (self.loan_start_date and self.loan_period):
frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting")) frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting"))
def validate_invoices(self):
discounted_invoices = [record.sales_invoice for record in
frappe.get_all("Discounted Invoice",fields = ["sales_invoice"], filters= {"docstatus":1})]
for record in self.invoices:
if record.sales_invoice in discounted_invoices:
frappe.throw("Row({0}): {1} is already discounted in {2}"
.format(record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent)))
def calculate_total_amount(self): def calculate_total_amount(self):
self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices]) self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices])
@ -212,7 +222,8 @@ def get_invoices(filters):
name as sales_invoice, name as sales_invoice,
customer, customer,
posting_date, posting_date,
outstanding_amount outstanding_amount,
debit_to
from `tabSales Invoice` si from `tabSales Invoice` si
where where
docstatus = 1 docstatus = 1

View File

@ -432,7 +432,6 @@ def get_customer_id(doc, customer=None):
return cust_id return cust_id
def make_customer_and_address(customers): def make_customer_and_address(customers):
customers_list = [] customers_list = []
for customer, data in iteritems(customers): for customer, data in iteritems(customers):
@ -449,7 +448,6 @@ def make_customer_and_address(customers):
frappe.db.commit() frappe.db.commit()
return customers_list return customers_list
def add_customer(data): def add_customer(data):
customer = data.get('full_name') or data.get('customer') customer = data.get('full_name') or data.get('customer')
if frappe.db.exists("Customer", customer.strip()): if frappe.db.exists("Customer", customer.strip()):
@ -466,21 +464,18 @@ def add_customer(data):
frappe.db.commit() frappe.db.commit()
return customer_doc.name return customer_doc.name
def get_territory(data): def get_territory(data):
if data.get('territory'): if data.get('territory'):
return data.get('territory') return data.get('territory')
return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories') return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories')
def get_customer_group(data): def get_customer_group(data):
if data.get('customer_group'): if data.get('customer_group'):
return data.get('customer_group') return data.get('customer_group')
return frappe.db.get_single_value('Selling Settings', 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name') return frappe.db.get_single_value('Selling Settings', 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name')
def make_contact(args, customer): def make_contact(args, customer):
if args.get('email_id') or args.get('phone'): if args.get('email_id') or args.get('phone'):
name = frappe.db.get_value('Dynamic Link', name = frappe.db.get_value('Dynamic Link',
@ -506,7 +501,6 @@ def make_contact(args, customer):
doc.flags.ignore_mandatory = True doc.flags.ignore_mandatory = True
doc.save(ignore_permissions=True) doc.save(ignore_permissions=True)
def make_address(args, customer): def make_address(args, customer):
if not args.get('address_line1'): if not args.get('address_line1'):
return return
@ -521,7 +515,10 @@ def make_address(args, customer):
address = frappe.get_doc('Address', name) address = frappe.get_doc('Address', name)
else: else:
address = frappe.new_doc('Address') address = frappe.new_doc('Address')
address.country = frappe.get_cached_value('Company', args.get('company'), 'country') if args.get('company'):
address.country = frappe.get_cached_value('Company',
args.get('company'), 'country')
address.append('links', { address.append('links', {
'link_doctype': 'Customer', 'link_doctype': 'Customer',
'link_name': customer 'link_name': customer
@ -533,7 +530,6 @@ def make_address(args, customer):
address.flags.ignore_mandatory = True address.flags.ignore_mandatory = True
address.save(ignore_permissions=True) address.save(ignore_permissions=True)
def make_email_queue(email_queue): def make_email_queue(email_queue):
name_list = [] name_list = []
for key, data in iteritems(email_queue): for key, data in iteritems(email_queue):
@ -550,7 +546,6 @@ def make_email_queue(email_queue):
return name_list return name_list
def validate_item(doc): def validate_item(doc):
for item in doc.get('items'): for item in doc.get('items'):
if not frappe.db.exists('Item', item.get('item_code')): if not frappe.db.exists('Item', item.get('item_code')):
@ -569,7 +564,6 @@ def validate_item(doc):
item_doc.save(ignore_permissions=True) item_doc.save(ignore_permissions=True)
frappe.db.commit() frappe.db.commit()
def submit_invoice(si_doc, name, doc, name_list): def submit_invoice(si_doc, name, doc, name_list):
try: try:
si_doc.insert() si_doc.insert()
@ -585,7 +579,6 @@ def submit_invoice(si_doc, name, doc, name_list):
return name_list return name_list
def save_invoice(doc, name, name_list): def save_invoice(doc, name, name_list):
try: try:
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}): if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):

View File

@ -206,9 +206,9 @@ class SalesInvoice(SellingController):
total_amount_in_payments = 0 total_amount_in_payments = 0
for payment in self.payments: for payment in self.payments:
total_amount_in_payments += payment.amount total_amount_in_payments += payment.amount
invoice_total = self.rounded_total or self.grand_total
if total_amount_in_payments < self.rounded_total: if total_amount_in_payments < invoice_total:
frappe.throw(_("Total payments amount can't be greater than {}".format(-self.rounded_total))) frappe.throw(_("Total payments amount can't be greater than {}".format(-invoice_total)))
def validate_pos_paid_amount(self): def validate_pos_paid_amount(self):
if len(self.payments) == 0 and self.is_pos: if len(self.payments) == 0 and self.is_pos:
@ -1510,4 +1510,4 @@ def create_invoice_discounting(source_name, target_doc=None):
"outstanding_amount": invoice.outstanding_amount "outstanding_amount": invoice.outstanding_amount
}) })
return invoice_discounting return invoice_discounting

View File

@ -1762,18 +1762,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.si_docs = this.get_submitted_invoice() || []; this.si_docs = this.get_submitted_invoice() || [];
this.email_queue_list = this.get_email_queue() || {}; this.email_queue_list = this.get_email_queue() || {};
this.customers_list = this.get_customers_details() || {}; this.customers_list = this.get_customers_details() || {};
if(this.customer_doc) {
this.freeze = this.customer_doc.display
}
freeze_screen = this.freeze_screen || false;
if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) {
this.freeze = true;
if (this.si_docs.length || this.email_queue_list || this.customers_list) {
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice", method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
freeze: freeze_screen, freeze: true,
args: { args: {
doc_list: me.si_docs, doc_list: me.si_docs,
email_queue_list: me.email_queue_list, email_queue_list: me.email_queue_list,

View File

@ -255,9 +255,15 @@ class Asset(AccountsController):
precision = self.precision("gross_purchase_amount") precision = self.precision("gross_purchase_amount")
if row.depreciation_method in ("Straight Line", "Manual"): if row.depreciation_method in ("Straight Line", "Manual"):
depreciation_left = (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked))
if not depreciation_left:
frappe.msgprint(_("All the depreciations has been booked"))
depreciation_amount = flt(row.expected_value_after_useful_life)
return depreciation_amount
depreciation_amount = (flt(row.value_after_depreciation) - depreciation_amount = (flt(row.value_after_depreciation) -
flt(row.expected_value_after_useful_life)) / (cint(row.total_number_of_depreciations) - flt(row.expected_value_after_useful_life)) / depreciation_left
cint(self.number_of_depreciations_booked))
else: else:
depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision) depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision)
@ -275,7 +281,7 @@ class Asset(AccountsController):
flt(accumulated_depreciation_after_full_schedule), flt(accumulated_depreciation_after_full_schedule),
self.precision('gross_purchase_amount')) self.precision('gross_purchase_amount'))
if (row.expected_value_after_useful_life and if (row.expected_value_after_useful_life and
row.expected_value_after_useful_life < asset_value_after_full_schedule): row.expected_value_after_useful_life < asset_value_after_full_schedule):
frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}") frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}")
.format(row.idx, asset_value_after_full_schedule)) .format(row.idx, asset_value_after_full_schedule))

View File

@ -41,6 +41,11 @@ def get_data():
"name": "Lead Source", "name": "Lead Source",
"description": _("Track Leads by Lead Source.") "description": _("Track Leads by Lead Source.")
}, },
{
"type": "doctype",
"name": "Contract",
"description": _("Helps you keep tracks of Contracts based on Supplier, Customer and Employee"),
},
] ]
}, },
{ {

View File

@ -45,6 +45,7 @@ class SellingController(StockController):
self.set_gross_profit() self.set_gross_profit()
set_default_income_account_for_item(self) set_default_income_account_for_item(self)
self.set_customer_address() self.set_customer_address()
self.validate_for_duplicate_items()
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
@ -381,6 +382,34 @@ class SellingController(StockController):
if self.get(address_field): if self.get(address_field):
self.set(address_display_field, get_address_display(self.get(address_field))) self.set(address_display_field, get_address_display(self.get(address_field)))
def validate_for_duplicate_items(self):
check_list, chk_dupl_itm = [], []
if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
return
for d in self.get('items'):
if self.doctype == "Sales Invoice":
e = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
f = [d.item_code, d.description, d.sales_order or d.delivery_note]
elif self.doctype == "Delivery Note":
e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
elif self.doctype == "Sales Order":
e = [d.item_code, d.description, d.warehouse, d.batch_no or '']
f = [d.item_code, d.description]
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
if e in check_list:
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
else:
check_list.append(e)
else:
if f in chk_dupl_itm:
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
else:
chk_dupl_itm.append(f)
def validate_items(self): def validate_items(self):
# validate items to see if they have is_sales_item enabled # validate items to see if they have is_sales_item enabled
from erpnext.controllers.buying_controller import validate_item_type from erpnext.controllers.buying_controller import validate_item_type

View File

@ -5,6 +5,8 @@ frappe.listview_settings['Leave Application'] = {
return [__("Approved"), "green", "status,=,Approved"]; return [__("Approved"), "green", "status,=,Approved"];
} else if (doc.status === "Rejected") { } else if (doc.status === "Rejected") {
return [__("Rejected"), "red", "status,=,Rejected"]; return [__("Rejected"), "red", "status,=,Rejected"];
} else {
return [__("Open"), "red", "status,=,Open"];
} }
} }
}; };

View File

@ -23,7 +23,8 @@ def get_columns():
_("Model") + ":data:50", _("Location") + ":data:100", _("Model") + ":data:50", _("Location") + ":data:100",
_("Log") + ":Link/Vehicle Log:100", _("Odometer") + ":Int:80", _("Log") + ":Link/Vehicle Log:100", _("Odometer") + ":Int:80",
_("Date") + ":Date:100", _("Fuel Qty") + ":Float:80", _("Date") + ":Date:100", _("Fuel Qty") + ":Float:80",
_("Fuel Price") + ":Float:100",_("Service Expense") + ":Float:100" _("Fuel Price") + ":Float:100",_("Fuel Expense") + ":Float:100",
_("Service Expense") + ":Float:100"
] ]
return columns return columns
@ -32,7 +33,8 @@ def get_log_data(filters):
data = frappe.db.sql("""select data = frappe.db.sql("""select
vhcl.license_plate as "License", vhcl.make as "Make", vhcl.model as "Model", vhcl.license_plate as "License", vhcl.make as "Make", vhcl.model as "Model",
vhcl.location as "Location", log.name as "Log", log.odometer as "Odometer", vhcl.location as "Location", log.name as "Log", log.odometer as "Odometer",
log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price" log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price",
log.fuel_qty * log.price as "Fuel Expense"
from from
`tabVehicle` vhcl,`tabVehicle Log` log `tabVehicle` vhcl,`tabVehicle Log` log
where where
@ -58,7 +60,7 @@ def get_chart_data(data,period_list):
total_ser_exp=0 total_ser_exp=0
for row in data: for row in data:
if row["Date"] <= period.to_date and row["Date"] >= period.from_date: if row["Date"] <= period.to_date and row["Date"] >= period.from_date:
total_fuel_exp+=flt(row["Fuel Price"]) total_fuel_exp+=flt(row["Fuel Expense"])
total_ser_exp+=flt(row["Service Expense"]) total_ser_exp+=flt(row["Service Expense"])
fueldata.append([period.key,total_fuel_exp]) fueldata.append([period.key,total_fuel_exp])
servicedata.append([period.key,total_ser_exp]) servicedata.append([period.key,total_ser_exp])
@ -84,4 +86,4 @@ def get_chart_data(data,period_list):
} }
} }
chart["type"] = "line" chart["type"] = "line"
return chart return chart

View File

@ -72,9 +72,7 @@ class SalesOrder(SellingController):
frappe.msgprint(_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(so[0][0], self.po_no)) frappe.msgprint(_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(so[0][0], self.po_no))
def validate_for_items(self): def validate_for_items(self):
check_list = []
for d in self.get('items'): for d in self.get('items'):
check_list.append(cstr(d.item_code))
# used for production plan # used for production plan
d.transaction_date = self.transaction_date d.transaction_date = self.transaction_date
@ -83,13 +81,6 @@ class SalesOrder(SellingController):
where item_code = %s and warehouse = %s", (d.item_code, d.warehouse)) where item_code = %s and warehouse = %s", (d.item_code, d.warehouse))
d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0 d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0
# check for same entry multiple times
unique_chk_list = set(check_list)
if len(unique_chk_list) != len(check_list) and \
not cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
frappe.msgprint(_("Same item has been entered multiple times"),
title=_("Warning"), indicator='orange')
def product_bundle_has_stock_item(self, product_bundle): def product_bundle_has_stock_item(self, product_bundle):
"""Returns true if product bundle has stock item""" """Returns true if product bundle has stock item"""
ret = len(frappe.db.sql("""select i.name from tabItem i, `tabProduct Bundle Item` pbi ret = len(frappe.db.sql("""select i.name from tabItem i, `tabProduct Bundle Item` pbi

View File

@ -166,24 +166,7 @@ class DeliveryNote(SellingController):
frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project)) frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project))
def validate_for_items(self): def validate_for_items(self):
check_list, chk_dupl_itm = [], []
if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
return
for d in self.get('items'): for d in self.get('items'):
e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
if e in check_list:
frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code))
else:
check_list.append(e)
else:
if f in chk_dupl_itm:
frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code))
else:
chk_dupl_itm.append(f)
#Customer Provided parts will have zero valuation rate #Customer Provided parts will have zero valuation rate
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1 d.allow_zero_valuation_rate = 1