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",
"column_break_3",
"posting_date",
"outstanding_amount"
"outstanding_amount",
"debit_to"
],
"fields": [
{
@ -48,10 +49,18 @@
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"fetch_from": "sales_invoice.debit_to",
"fieldname": "debit_to",
"fieldtype": "Link",
"label": "Debit to",
"options": "Account",
"read_only": 1
}
],
"istable": 1,
"modified": "2019-05-30 19:27:29.436153",
"modified": "2019-08-07 15:13:55.808349",
"modified_by": "Administrator",
"module": "Accounts",
"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("short_term_loan", frm, {"root_type": "Liability"});
frm.events.filter_accounts("accounts_receivable_credit", 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("bank_account", frm, [["account_type", "=", "Bank"]]);
frm.events.filter_accounts("bank_charges_account", frm, [["root_type", "=", "Expense"]]);
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_credit", frm, [["account_type", "=", "Receivable"]]);
frm.events.filter_accounts("accounts_receivable_unpaid", frm, [["account_type", "=", "Receivable"]]);
},
filter_accounts: (fieldname, frm, addl_filters) => {
let filters = {
"company": frm.doc.company,
"is_group": 0
};
if(addl_filters) Object.assign(filters, addl_filters);
let filters = [
["company", "=", frm.doc.company],
["is_group", "=", 0]
];
if(addl_filters){
filters = $.merge(filters , addl_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) => {
frm.events.show_general_ledger(frm);
if(frm.doc.docstatus === 0) {
if (frm.doc.docstatus === 0) {
frm.add_custom_button(__('Get Invoices'), function() {
frm.events.get_invoices(frm);
});
}
if(frm.doc.docstatus === 1 && frm.doc.status !== "Settled") {
if(frm.doc.status == "Sanctioned") {
if (frm.doc.docstatus === 1 && frm.doc.status !== "Settled") {
if (frm.doc.status == "Sanctioned") {
frm.add_custom_button(__('Disburse Loan'), function() {
frm.events.create_disbursement_entry(frm);
}).addClass("btn-primary");
}
if(frm.doc.status == "Disbursed") {
if (frm.doc.status == "Disbursed") {
frm.add_custom_button(__('Close Loan'), function() {
frm.events.close_loan(frm);
}).addClass("btn-primary");
@ -64,7 +80,7 @@ frappe.ui.form.on('Invoice Discounting', {
},
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);
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);
let row = frm.add_child("invoices");
$.extend(row, v);
frm.events.refresh_filters(frm);
});
refresh_field("invoices");
}
@ -190,8 +207,10 @@ frappe.ui.form.on('Invoice Discounting', {
frappe.ui.form.on('Discounted Invoice', {
sales_invoice: (frm) => {
frm.events.calculate_total_amount(frm);
frm.events.refresh_filters(frm);
},
invoices_remove: (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):
def validate(self):
self.validate_mandatory()
self.validate_invoices()
self.calculate_total_amount()
self.set_status()
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):
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):
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,
customer,
posting_date,
outstanding_amount
outstanding_amount,
debit_to
from `tabSales Invoice` si
where
docstatus = 1

View File

@ -432,7 +432,6 @@ def get_customer_id(doc, customer=None):
return cust_id
def make_customer_and_address(customers):
customers_list = []
for customer, data in iteritems(customers):
@ -449,7 +448,6 @@ def make_customer_and_address(customers):
frappe.db.commit()
return customers_list
def add_customer(data):
customer = data.get('full_name') or data.get('customer')
if frappe.db.exists("Customer", customer.strip()):
@ -466,21 +464,18 @@ def add_customer(data):
frappe.db.commit()
return customer_doc.name
def get_territory(data):
if data.get('territory'):
return data.get('territory')
return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories')
def get_customer_group(data):
if 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')
def make_contact(args, customer):
if args.get('email_id') or args.get('phone'):
name = frappe.db.get_value('Dynamic Link',
@ -506,7 +501,6 @@ def make_contact(args, customer):
doc.flags.ignore_mandatory = True
doc.save(ignore_permissions=True)
def make_address(args, customer):
if not args.get('address_line1'):
return
@ -521,7 +515,10 @@ def make_address(args, customer):
address = frappe.get_doc('Address', name)
else:
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', {
'link_doctype': 'Customer',
'link_name': customer
@ -533,7 +530,6 @@ def make_address(args, customer):
address.flags.ignore_mandatory = True
address.save(ignore_permissions=True)
def make_email_queue(email_queue):
name_list = []
for key, data in iteritems(email_queue):
@ -550,7 +546,6 @@ def make_email_queue(email_queue):
return name_list
def validate_item(doc):
for item in doc.get('items'):
if not frappe.db.exists('Item', item.get('item_code')):
@ -569,7 +564,6 @@ def validate_item(doc):
item_doc.save(ignore_permissions=True)
frappe.db.commit()
def submit_invoice(si_doc, name, doc, name_list):
try:
si_doc.insert()
@ -585,7 +579,6 @@ def submit_invoice(si_doc, name, doc, name_list):
return name_list
def save_invoice(doc, name, name_list):
try:
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
for payment in self.payments:
total_amount_in_payments += payment.amount
if total_amount_in_payments < self.rounded_total:
frappe.throw(_("Total payments amount can't be greater than {}".format(-self.rounded_total)))
invoice_total = self.rounded_total or self.grand_total
if total_amount_in_payments < invoice_total:
frappe.throw(_("Total payments amount can't be greater than {}".format(-invoice_total)))
def validate_pos_paid_amount(self):
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
})
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.email_queue_list = this.get_email_queue() || {};
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({
method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
freeze: freeze_screen,
freeze: true,
args: {
doc_list: me.si_docs,
email_queue_list: me.email_queue_list,

View File

@ -255,9 +255,15 @@ class Asset(AccountsController):
precision = self.precision("gross_purchase_amount")
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) -
flt(row.expected_value_after_useful_life)) / (cint(row.total_number_of_depreciations) -
cint(self.number_of_depreciations_booked))
flt(row.expected_value_after_useful_life)) / depreciation_left
else:
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),
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):
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))

View File

@ -41,6 +41,11 @@ def get_data():
"name": "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()
set_default_income_account_for_item(self)
self.set_customer_address()
self.validate_for_duplicate_items()
def set_missing_values(self, for_validate=False):
@ -381,6 +382,34 @@ class SellingController(StockController):
if 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):
# validate items to see if they have is_sales_item enabled
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"];
} else if (doc.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",
_("Log") + ":Link/Vehicle Log:100", _("Odometer") + ":Int: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
@ -32,7 +33,8 @@ def get_log_data(filters):
data = frappe.db.sql("""select
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",
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
`tabVehicle` vhcl,`tabVehicle Log` log
where
@ -58,7 +60,7 @@ def get_chart_data(data,period_list):
total_ser_exp=0
for row in data:
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"])
fueldata.append([period.key,total_fuel_exp])
servicedata.append([period.key,total_ser_exp])
@ -84,4 +86,4 @@ def get_chart_data(data,period_list):
}
}
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))
def validate_for_items(self):
check_list = []
for d in self.get('items'):
check_list.append(cstr(d.item_code))
# used for production plan
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))
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):
"""Returns true if product bundle has stock item"""
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))
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'):
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
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1