Merge branch 'develop' of github.com:frappe/erpnext into feature-pick-list

This commit is contained in:
Suraj Shetty 2019-08-26 10:51:12 +05:30
commit 8de8a78235
34 changed files with 218 additions and 373 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.0.7' __version__ = '12.0.8'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@ -128,6 +128,7 @@ class Account(NestedSet):
"account_currency": self.account_currency, "account_currency": self.account_currency,
"parent_account": parent_acc_name_map[company] "parent_account": parent_acc_name_map[company]
}) })
if not self.check_if_child_acc_exists(doc):
doc.save() doc.save()
frappe.msgprint(_("Account {0} is added in the child company {1}") frappe.msgprint(_("Account {0} is added in the child company {1}")
.format(doc.name, company)) .format(doc.name, company))
@ -172,6 +173,24 @@ class Account(NestedSet):
if frappe.db.get_value("GL Entry", {"account": self.name}): if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency")) frappe.throw(_("Currency can not be changed after making entries using some other currency"))
def check_if_child_acc_exists(self, doc):
''' Checks if a account in parent company exists in the '''
info = frappe.db.get_value("Account", {
"account_name": doc.account_name,
"account_number": doc.account_number
}, ['company', 'account_currency', 'is_group', 'root_type', 'account_type', 'balance_must_be', 'account_name'], as_dict=1)
if not info:
return
doc = vars(doc)
dict_diff = [k for k in info if k in doc and info[k] != doc[k] and k != "company"]
if dict_diff:
frappe.throw(_("Account {0} already exists in child company {1}. The following fields have different values, they should be same:<ul><li>{2}</li></ul>")
.format(info.account_name, info.company, '</li><li>'.join(dict_diff)))
else:
return True
def convert_group_to_ledger(self): def convert_group_to_ledger(self):
if self.check_if_child_exists(): if self.check_if_child_exists():
throw(_("Account with child nodes cannot be converted to ledger")) throw(_("Account with child nodes cannot be converted to ledger"))

View File

@ -50,7 +50,7 @@ class BankTransaction(StatusUpdater):
if paid_amount and allocated_amount: if paid_amount and allocated_amount:
if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount): if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount):
frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).".format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount)))) frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).".format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount))))
elif flt(allocated_amount[0]["allocated_amount"]) == flt(paid_amount): else:
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]: if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
self.clear_simple_entry(payment_entry) self.clear_simple_entry(payment_entry)

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,25 +13,41 @@ 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);
@ -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

@ -1,29 +0,0 @@
frappe.ui.form.on('Payment Order', {
refresh: function(frm) {
if (frm.doc.docstatus==1 && frm.doc.payment_order_type==='Payment Entry') {
frm.add_custom_button(__('Generate Text File'), function() {
frm.trigger("generate_text_and_download_file");
});
}
},
generate_text_and_download_file: (frm) => {
return frappe.call({
method: "erpnext.regional.india.bank_remittance.generate_report",
args: {
name: frm.doc.name
},
freeze: true,
callback: function(r) {
{
frm.reload_doc();
const a = document.createElement('a');
let file_obj = r.message;
a.href = file_obj.file_url;
a.target = '_blank';
a.download = file_obj.file_name;
a.click();
}
}
});
}
});

View File

@ -257,11 +257,8 @@ def reconcile_dr_cr_note(dr_cr_notes):
voucher_type = ('Credit Note' voucher_type = ('Credit Note'
if d.voucher_type == 'Sales Invoice' else 'Debit Note') if d.voucher_type == 'Sales Invoice' else 'Debit Note')
dr_or_cr = ('credit_in_account_currency'
if d.reference_type == 'Sales Invoice' else 'debit_in_account_currency')
reconcile_dr_or_cr = ('debit_in_account_currency' reconcile_dr_or_cr = ('debit_in_account_currency'
if dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency') if d.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency')
jv = frappe.get_doc({ jv = frappe.get_doc({
"doctype": "Journal Entry", "doctype": "Journal Entry",
@ -272,8 +269,7 @@ def reconcile_dr_cr_note(dr_cr_notes):
'account': d.account, 'account': d.account,
'party': d.party, 'party': d.party,
'party_type': d.party_type, 'party_type': d.party_type,
reconcile_dr_or_cr: (abs(d.allocated_amount) d.dr_or_cr: abs(d.allocated_amount),
if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)),
'reference_type': d.against_voucher_type, 'reference_type': d.against_voucher_type,
'reference_name': d.against_voucher 'reference_name': d.against_voucher
}, },
@ -281,7 +277,8 @@ def reconcile_dr_cr_note(dr_cr_notes):
'account': d.account, 'account': d.account,
'party': d.party, 'party': d.party,
'party_type': d.party_type, 'party_type': d.party_type,
dr_or_cr: abs(d.allocated_amount), reconcile_dr_or_cr: (abs(d.allocated_amount)
if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)),
'reference_type': d.voucher_type, 'reference_type': d.voucher_type,
'reference_name': d.voucher_no 'reference_name': d.voucher_no
} }

View File

@ -307,7 +307,7 @@ def get_item_tax_data():
# example: {'Consulting Services': {'Excise 12 - TS': '12.000'}} # example: {'Consulting Services': {'Excise 12 - TS': '12.000'}}
itemwise_tax = {} itemwise_tax = {}
taxes = frappe.db.sql(""" select parent, tax_type, tax_rate from `tabItem Tax`""", as_dict=1) taxes = frappe.db.sql(""" select parent, tax_type, tax_rate from `tabItem Tax Template Detail`""", as_dict=1)
for tax in taxes: for tax in taxes:
if tax.parent not in itemwise_tax: if tax.parent not in itemwise_tax:
@ -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:

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

@ -197,8 +197,10 @@ class ReceivablePayableReport(object):
if self.filters.based_on_payment_terms and gl_entries_data: if self.filters.based_on_payment_terms and gl_entries_data:
self.payment_term_map = self.get_payment_term_detail(voucher_nos) self.payment_term_map = self.get_payment_term_detail(voucher_nos)
self.gle_inclusion_map = {}
for gle in gl_entries_data: for gle in gl_entries_data:
if self.is_receivable_or_payable(gle, self.dr_or_cr, future_vouchers, return_entries): if self.is_receivable_or_payable(gle, self.dr_or_cr, future_vouchers, return_entries):
self.gle_inclusion_map[gle.name] = True
outstanding_amount, credit_note_amount, payment_amount = self.get_outstanding_amount( outstanding_amount, credit_note_amount, payment_amount = self.get_outstanding_amount(
gle,self.filters.report_date, self.dr_or_cr, return_entries) gle,self.filters.report_date, self.dr_or_cr, return_entries)
temp_outstanding_amt = outstanding_amount temp_outstanding_amt = outstanding_amount
@ -409,7 +411,9 @@ class ReceivablePayableReport(object):
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no): for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
if getdate(e.posting_date) <= report_date \ if getdate(e.posting_date) <= report_date \
and (e.name!=gle.name or (e.voucher_no in return_entries and not return_entries.get(e.voucher_no))): and (e.name!=gle.name or (e.voucher_no in return_entries and not return_entries.get(e.voucher_no))):
if e.name!=gle.name and self.gle_inclusion_map.get(e.name):
continue
self.gle_inclusion_map[e.name] = True
amount = flt(e.get(reverse_dr_or_cr), self.currency_precision) - flt(e.get(dr_or_cr), self.currency_precision) amount = flt(e.get(reverse_dr_or_cr), self.currency_precision) - flt(e.get(dr_or_cr), self.currency_precision)
if e.voucher_no not in return_entries: if e.voucher_no not in return_entries:
payment_amount += amount payment_amount += amount

View File

@ -135,11 +135,11 @@ def get_chart_data(filters, columns, asset, liability, equity):
datasets = [] datasets = []
if asset_data: if asset_data:
datasets.append({'name':'Assets', 'values': asset_data}) datasets.append({'name': _('Assets'), 'values': asset_data})
if liability_data: if liability_data:
datasets.append({'name':'Liabilities', 'values': liability_data}) datasets.append({'name': _('Liabilities'), 'values': liability_data})
if equity_data: if equity_data:
datasets.append({'name':'Equity', 'values': equity_data}) datasets.append({'name': _('Equity'), 'values': equity_data})
chart = { chart = {
"data": { "data": {

View File

@ -27,8 +27,8 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
fieldname:"payment_type", fieldname:"payment_type",
label: __("Payment Type"), label: __("Payment Type"),
fieldtype: "Select", fieldtype: "Select",
options: "Incoming\nOutgoing", options: __("Incoming") + "\n" + __("Outgoing"),
default: "Incoming" default: __("Incoming")
}, },
{ {
"fieldname":"party_type", "fieldname":"party_type",

View File

@ -39,8 +39,8 @@ def execute(filters=None):
return columns, data return columns, data
def validate_filters(filters): def validate_filters(filters):
if (filters.get("payment_type") == "Incoming" and filters.get("party_type") == "Supplier") or \ if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or \
(filters.get("payment_type") == "Outgoing" and filters.get("party_type") == "Customer"): (filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"):
frappe.throw(_("{0} payment entries can not be filtered by {1}")\ frappe.throw(_("{0} payment entries can not be filtered by {1}")\
.format(filters.payment_type, filters.party_type)) .format(filters.payment_type, filters.party_type))
@ -51,7 +51,7 @@ def get_columns(filters):
_("Party Type") + "::100", _("Party Type") + "::100",
_("Party") + ":Dynamic Link/Party Type:140", _("Party") + ":Dynamic Link/Party Type:140",
_("Posting Date") + ":Date:100", _("Posting Date") + ":Date:100",
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"), _("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == _("Outgoing") else ":Link/Sales Invoice:130"),
_("Invoice Posting Date") + ":Date:130", _("Invoice Posting Date") + ":Date:130",
_("Payment Due Date") + ":Date:130", _("Payment Due Date") + ":Date:130",
_("Debit") + ":Currency:120", _("Debit") + ":Currency:120",
@ -69,7 +69,7 @@ def get_conditions(filters):
conditions = [] conditions = []
if not filters.party_type: if not filters.party_type:
if filters.payment_type == "Outgoing": if filters.payment_type == _("Outgoing"):
filters.party_type = "Supplier" filters.party_type = "Supplier"
else: else:
filters.party_type = "Customer" filters.party_type = "Customer"
@ -101,7 +101,7 @@ def get_entries(filters):
def get_invoice_posting_date_map(filters): def get_invoice_posting_date_map(filters):
invoice_details = {} invoice_details = {}
dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice" dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice"
for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1): for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
invoice_details[t.name] = t invoice_details[t.name] = t

View File

@ -75,11 +75,11 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
datasets = [] datasets = []
if income_data: if income_data:
datasets.append({'name': 'Income', 'values': income_data}) datasets.append({'name': _('Income'), 'values': income_data})
if expense_data: if expense_data:
datasets.append({'name': 'Expense', 'values': expense_data}) datasets.append({'name': _('Expense'), 'values': expense_data})
if net_profit: if net_profit:
datasets.append({'name': 'Net Profit/Loss', 'values': net_profit}) datasets.append({'name': _('Net Profit/Loss'), 'values': net_profit})
chart = { chart = {
"data": { "data": {

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)

View File

@ -6,15 +6,13 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.crm.doctype.utils import get_scheduled_employees_for_popup from erpnext.crm.doctype.utils import get_scheduled_employees_for_popup, strip_number
from frappe.contacts.doctype.contact.contact import get_contact_with_phone_number from frappe.contacts.doctype.contact.contact import get_contact_with_phone_number
from erpnext.crm.doctype.lead.lead import get_lead_with_phone_number from erpnext.crm.doctype.lead.lead import get_lead_with_phone_number
class CallLog(Document): class CallLog(Document):
def before_insert(self): def before_insert(self):
# strip 0 from the start of the number for proper number comparisions number = strip_number(self.get('from'))
# eg. 07888383332 should match with 7888383332
number = self.get('from').lstrip('0')
self.contact = get_contact_with_phone_number(number) self.contact = get_contact_with_phone_number(number)
self.lead = get_lead_with_phone_number(number) self.lead = get_lead_with_phone_number(number)
@ -48,13 +46,14 @@ def add_call_summary(call_log, summary):
doc.add_comment('Comment', frappe.bold(_('Call Summary')) + '<br><br>' + summary) doc.add_comment('Comment', frappe.bold(_('Call Summary')) + '<br><br>' + summary)
def get_employees_with_number(number): def get_employees_with_number(number):
number = strip_number(number)
if not number: return [] if not number: return []
employee_emails = frappe.cache().hget('employees_with_number', number) employee_emails = frappe.cache().hget('employees_with_number', number)
if employee_emails: return employee_emails if employee_emails: return employee_emails
employees = frappe.get_all('Employee', filters={ employees = frappe.get_all('Employee', filters={
'cell_number': ['like', '%{}'.format(number.lstrip('0'))], 'cell_number': ['like', '%{}%'.format(number)],
'user_id': ['!=', ''] 'user_id': ['!=', '']
}, fields=['user_id']) }, fields=['user_id'])
@ -64,23 +63,29 @@ def get_employees_with_number(number):
return employee return employee
def set_caller_information(doc, state): def set_caller_information(doc, state):
'''Called from hoooks on creation of Lead or Contact''' '''Called from hooks on creation of Lead or Contact'''
if doc.doctype not in ['Lead', 'Contact']: return if doc.doctype not in ['Lead', 'Contact']: return
numbers = [doc.get('phone'), doc.get('mobile_no')] numbers = [doc.get('phone'), doc.get('mobile_no')]
for_doc = doc.doctype.lower() # contact for Contact and lead for Lead
fieldname = doc.doctype.lower()
# contact_name or lead_name
display_name_field = '{}_name'.format(fieldname)
for number in numbers: for number in numbers:
number = strip_number(number)
if not number: continue if not number: continue
print(number)
filters = frappe._dict({ filters = frappe._dict({
'from': ['like', '%{}'.format(number.lstrip('0'))], 'from': ['like', '%{}'.format(number)],
for_doc: '' fieldname: ''
}) })
logs = frappe.get_all('Call Log', filters=filters) logs = frappe.get_all('Call Log', filters=filters)
for log in logs: for log in logs:
call_log = frappe.get_doc('Call Log', log.name) frappe.db.set_value('Call Log', log.name, {
call_log.set(for_doc, doc.name) fieldname: doc.name,
call_log.save(ignore_permissions=True) display_name_field: doc.get_title()
}, update_modified=False)

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

@ -371,7 +371,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select tabAccount.name from `tabAccount` return frappe.db.sql("""select tabAccount.name from `tabAccount`
where (tabAccount.report_type = "Profit and Loss" where (tabAccount.report_type = "Profit and Loss"
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed")) or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress"))
and tabAccount.is_group=0 and tabAccount.is_group=0
and tabAccount.docstatus!=2 and tabAccount.docstatus!=2
and tabAccount.{key} LIKE %(txt)s and tabAccount.{key} LIKE %(txt)s

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

@ -54,6 +54,8 @@ def get_last_issue_from_customer(customer_name):
def get_scheduled_employees_for_popup(communication_medium): def get_scheduled_employees_for_popup(communication_medium):
if not communication_medium: return []
now_time = frappe.utils.nowtime() now_time = frappe.utils.nowtime()
weekday = frappe.utils.get_weekday() weekday = frappe.utils.get_weekday()
@ -73,3 +75,10 @@ def get_scheduled_employees_for_popup(communication_medium):
employee_emails = set([employee.user_id for employee in employees]) employee_emails = set([employee.user_id for employee in employees])
return employee_emails return employee_emails
def strip_number(number):
if not number: return
# strip 0 from the start of the number for proper number comparisions
# eg. 07888383332 should match with 7888383332
number = number.lstrip('0')
return number

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])

View File

@ -605,7 +605,6 @@ erpnext.patches.v11_1.rename_depends_on_lwp
execute:frappe.delete_doc("Report", "Inactive Items") execute:frappe.delete_doc("Report", "Inactive Items")
erpnext.patches.v11_1.delete_scheduling_tool erpnext.patches.v11_1.delete_scheduling_tool
erpnext.patches.v12_0.rename_tolerance_fields erpnext.patches.v12_0.rename_tolerance_fields
erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019
execute:frappe.delete_doc_if_exists("Page", "support-analytics") execute:frappe.delete_doc_if_exists("Page", "support-analytics")
erpnext.patches.v12_0.remove_patient_medical_record_page erpnext.patches.v12_0.remove_patient_medical_record_page
erpnext.patches.v11_1.move_customer_lead_to_dynamic_column erpnext.patches.v11_1.move_customer_lead_to_dynamic_column
@ -626,4 +625,5 @@ erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
erpnext.patches.v12_0.update_ewaybill_field_position erpnext.patches.v12_0.update_ewaybill_field_position
erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes
erpnext.patches.v11_1.set_status_for_material_request_type_manufacture erpnext.patches.v11_1.set_status_for_material_request_type_manufacture
erpnext.patches.v12_0.remove_bank_remittance_custom_fields
erpnext.patches.v12_0.generate_leave_ledger_entries erpnext.patches.v12_0.generate_leave_ledger_entries

View File

@ -1,12 +0,0 @@
from __future__ import unicode_literals
import frappe
from erpnext.regional.india.setup import make_custom_fields
def execute():
frappe.reload_doc("accounts", "doctype", "tax_category")
frappe.reload_doc("stock", "doctype", "item_manufacturer")
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
make_custom_fields()

View File

@ -0,0 +1,14 @@
from __future__ import unicode_literals
import frappe
from erpnext.regional.india.setup import make_custom_fields
def execute():
frappe.reload_doc("accounts", "doctype", "tax_category")
frappe.reload_doc("stock", "doctype", "item_manufacturer")
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
if frappe.db.exists("Custom Field", "Company-bank_remittance_section"):
deprecated_fields = ['bank_remittance_section', 'client_code', 'remittance_column_break', 'product_code']
for i in range(len(deprecated_fields)):
frappe.delete_doc("Custom Field", 'Company-'+deprecated_fields[i])

View File

@ -1,190 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.utils import cint,cstr, today
from frappe import _
import re
import datetime
from collections import OrderedDict
def create_bank_remittance_txt(name):
payment_order = frappe.get_cached_doc("Payment Order", name)
no_of_records = len(payment_order.get("references"))
total_amount = sum(entry.get("amount") for entry in payment_order.get("references"))
product_code, client_code, company_email = frappe.db.get_value("Company",
filters={'name' : payment_order.company},
fieldname=['product_code', 'client_code', 'email'])
header, file_name = get_header_row(payment_order, client_code)
batch = get_batch_row(payment_order, no_of_records, total_amount, product_code)
detail = []
for ref_doc in payment_order.get("references"):
detail += get_detail_row(ref_doc, payment_order, company_email)
trailer = get_trailer_row(no_of_records, total_amount)
detail_records = "\n".join(detail)
return "\n".join([header, batch, detail_records, trailer]), file_name
@frappe.whitelist()
def generate_report(name):
data, file_name = create_bank_remittance_txt(name)
f = frappe.get_doc({
'doctype': 'File',
'file_name': file_name,
'content': data,
"attached_to_doctype": 'Payment Order',
"attached_to_name": name,
'is_private': True
})
f.save()
return {
'file_url': f.file_url,
'file_name': file_name
}
def generate_file_name(name, company_account, date):
''' generate file name with format (account_code)_mmdd_(payment_order_no) '''
bank, acc_no = frappe.db.get_value("Bank Account", {"name": company_account}, ['bank', 'bank_account_no'])
return bank[:1]+str(acc_no)[-4:]+'_'+date.strftime("%m%d")+sanitize_data(name, '')[4:]+'.txt'
def get_header_row(doc, client_code):
''' Returns header row and generated file name '''
file_name = generate_file_name(doc.name, doc.company_bank_account, doc.posting_date)
header = ["H"]
header.append(validate_field_size(client_code, "Client Code", 20))
header += [''] * 3
header.append(validate_field_size(file_name, "File Name", 20))
return "~".join(header), file_name
def get_batch_row(doc, no_of_records, total_amount, product_code):
batch = ["B"]
batch.append(validate_field_size(no_of_records, "No Of Records", 5))
batch.append(validate_amount(format(total_amount, '0.2f'), 17))
batch.append(sanitize_data(doc.name, '_')[:20])
batch.append(format_date(doc.posting_date))
batch.append(validate_field_size(product_code,"Product Code", 20))
return "~".join(batch)
def get_detail_row(ref_doc, payment_entry, company_email):
payment_date = format_date(payment_entry.posting_date)
payment_entry = frappe.get_cached_doc('Payment Entry', ref_doc.payment_entry)
supplier_bank_details = frappe.get_cached_doc('Bank Account', ref_doc.bank_account)
company_bank_acc_no = frappe.db.get_value("Bank Account", {'name': payment_entry.bank_account}, ['bank_account_no'])
addr_link = frappe.db.get_value('Dynamic Link',
{
'link_doctype': 'Supplier',
'link_name': 'Sample Supplier',
'parenttype':'Address',
'parent': ('like', '%-Billing')
}, 'parent')
supplier_billing_address = frappe.get_cached_doc('Address', addr_link)
email = ','.join(filter(None, [supplier_billing_address.email_id, company_email]))
detail = OrderedDict(
record_identifier='D',
payment_ref_no=sanitize_data(ref_doc.payment_entry),
payment_type=cstr(payment_entry.mode_of_payment)[:10],
amount=str(validate_amount(format(ref_doc.amount, '.2f'),13)),
payment_date=payment_date,
instrument_date=payment_date,
instrument_number='',
dr_account_no_client=str(validate_field_size(company_bank_acc_no, "Company Bank Account", 20)),
dr_description='',
dr_ref_no='',
cr_ref_no='',
bank_code_indicator='M',
beneficiary_code='',
beneficiary_name=sanitize_data(validate_information(payment_entry, "party", 160), ' '),
beneficiary_bank=sanitize_data(validate_information(supplier_bank_details, "bank", 10)),
beneficiary_branch_code=cstr(validate_information(supplier_bank_details, "branch_code", 11)),
beneficiary_acc_no=validate_information(supplier_bank_details, "bank_account_no", 20),
location='',
print_location='',
beneficiary_address_1=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line1), ' '), " Beneficiary Address 1", 50),
beneficiary_address_2=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line2), ' '), " Beneficiary Address 2", 50),
beneficiary_address_3='',
beneficiary_address_4='',
beneficiary_address_5='',
beneficiary_city=validate_field_size(cstr(supplier_billing_address.city), "Beneficiary City", 20),
beneficiary_zipcode=validate_field_size(cstr(supplier_billing_address.pincode), "Pin Code", 6),
beneficiary_state=validate_field_size(cstr(supplier_billing_address.state), "Beneficiary State", 20),
beneficiary_email=cstr(email)[:255],
beneficiary_mobile=validate_field_size(cstr(supplier_billing_address.phone), "Beneficiary Mobile", 10),
payment_details_1='',
payment_details_2='',
payment_details_3='',
payment_details_4='',
delivery_mode=''
)
detail_record = ["~".join(list(detail.values()))]
detail_record += get_advice_rows(payment_entry)
return detail_record
def get_advice_rows(payment_entry):
''' Returns multiple advice rows for a single detail entry '''
payment_entry_date = payment_entry.posting_date.strftime("%b%y%d%m").upper()
mode_of_payment = payment_entry.mode_of_payment
advice_rows = []
for record in payment_entry.references:
advice = ['E']
advice.append(cstr(mode_of_payment))
advice.append(cstr(record.total_amount))
advice.append('')
advice.append(cstr(record.outstanding_amount))
advice.append(record.reference_name)
advice.append(format_date(record.due_date))
advice.append(payment_entry_date)
advice_rows.append("~".join(advice))
return advice_rows
def get_trailer_row(no_of_records, total_amount):
''' Returns trailer row '''
trailer = ["T"]
trailer.append(validate_field_size(no_of_records, "No of Records", 5))
trailer.append(validate_amount(format(total_amount, "0.2f"), 17))
return "~".join(trailer)
def sanitize_data(val, replace_str=''):
''' Remove all the non-alphanumeric characters from string '''
pattern = re.compile('[\W_]+')
return pattern.sub(replace_str, val)
def format_date(val):
''' Convert a datetime object to DD/MM/YYYY format '''
return val.strftime("%d/%m/%Y")
def validate_amount(val, max_int_size):
''' Validate amount to be within the allowed limits '''
int_size = len(str(val).split('.')[0])
if int_size > max_int_size:
frappe.throw(_("Amount for a single transaction exceeds maximum allowed amount, create a separate payment order by splitting the transactions"))
return val
def validate_information(obj, attr, max_size):
''' Checks if the information is not set in the system and is within the size '''
if hasattr(obj, attr):
return validate_field_size(getattr(obj, attr), frappe.unscrub(attr), max_size)
else:
frappe.throw(_("{0} is mandatory for generating remittance payments, set the field and try again".format(frappe.unscrub(attr))))
def validate_field_size(val, label, max_size):
''' check the size of the val '''
if len(cstr(val)) > max_size:
frappe.throw(_("{0} field is limited to size {1}".format(label, max_size)))
return cstr(val)

View File

@ -407,14 +407,6 @@ def make_custom_fields(update=True):
fieldtype='Link', options='Salary Component', insert_after='basic_component'), fieldtype='Link', options='Salary Component', insert_after='basic_component'),
dict(fieldname='arrear_component', label='Arrear Component', dict(fieldname='arrear_component', label='Arrear Component',
fieldtype='Link', options='Salary Component', insert_after='hra_component'), fieldtype='Link', options='Salary Component', insert_after='hra_component'),
dict(fieldname='bank_remittance_section', label='Bank Remittance Settings',
fieldtype='Section Break', collapsible=1, insert_after='arrear_component'),
dict(fieldname='client_code', label='Client Code', fieldtype='Data',
insert_after='bank_remittance_section'),
dict(fieldname='remittance_column_break', fieldtype='Column Break',
insert_after='client_code'),
dict(fieldname='product_code', label='Product Code', fieldtype='Data',
insert_after='remittance_column_break'),
], ],
'Employee Tax Exemption Declaration':[ 'Employee Tax Exemption Declaration':[
dict(fieldname='hra_section', label='HRA Exemption', dict(fieldname='hra_section', label='HRA Exemption',

View File

@ -156,33 +156,21 @@ class Gstr1Report(object):
if self.filters.get("type_of_business") == "B2B": if self.filters.get("type_of_business") == "B2B":
customers = frappe.get_all("Customer", conditions += "and ifnull(gst_category, '') in ('Registered Regular', 'Deemed Export', 'SEZ') and is_return != 1"
filters={
"gst_category": ["in", ["Registered Regular", "Deemed Export", "SEZ"]]
})
if customers:
conditions += """ and ifnull(gst_category, '') != 'Overseas' and is_return != 1
and customer in ({0})""".format(", ".join([frappe.db.escape(c.name) for c in customers]))
if self.filters.get("type_of_business") in ("B2C Large", "B2C Small"): if self.filters.get("type_of_business") in ("B2C Large", "B2C Small"):
b2c_limit = frappe.db.get_single_value('GST Settings', 'b2c_limit') b2c_limit = frappe.db.get_single_value('GST Settings', 'b2c_limit')
if not b2c_limit: if not b2c_limit:
frappe.throw(_("Please set B2C Limit in GST Settings.")) frappe.throw(_("Please set B2C Limit in GST Settings."))
customers = frappe.get_all("Customer",
filters={
"gst_category": ["in", ["Unregistered"]]
})
if self.filters.get("type_of_business") == "B2C Large" and customers: if self.filters.get("type_of_business") == "B2C Large" and customers:
conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2) conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2)
and grand_total > {0} and is_return != 1 and customer in ({1})""".\ and grand_total > {0} and is_return != 1 and gst_category ='Unregistered' """.\
format(flt(b2c_limit), ", ".join([frappe.db.escape(c.name) for c in customers])) format(flt(b2c_limit), ", ".join([frappe.db.escape(c.name) for c in customers]))
elif self.filters.get("type_of_business") == "B2C Small" and customers: elif self.filters.get("type_of_business") == "B2C Small" and customers:
conditions += """ and ( conditions += """ and (
SUBSTR(place_of_supply, 1, 2) = SUBSTR(company_gstin, 1, 2) SUBSTR(place_of_supply, 1, 2) = SUBSTR(company_gstin, 1, 2)
or grand_total <= {0}) and is_return != 1 and customer in ({1})""".\ or grand_total <= {0}) and is_return != 1 and gst_category ='Unregistered' """.\
format(flt(b2c_limit), ", ".join([frappe.db.escape(c.name) for c in customers])) format(flt(b2c_limit), ", ".join([frappe.db.escape(c.name) for c in customers]))
elif self.filters.get("type_of_business") == "CDNR": elif self.filters.get("type_of_business") == "CDNR":

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

@ -4,7 +4,7 @@ frappe.provide('erpnext.pos');
frappe.pages['point-of-sale'].on_page_load = function(wrapper) { frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
frappe.ui.make_app_page({ frappe.ui.make_app_page({
parent: wrapper, parent: wrapper,
title: 'Point of Sale', title: __('Point of Sale'),
single_column: true single_column: true
}); });

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

View File

@ -124,6 +124,7 @@ class Item(WebsiteGenerator):
self.update_defaults_from_item_group() self.update_defaults_from_item_group()
self.validate_auto_reorder_enabled_in_stock_settings() self.validate_auto_reorder_enabled_in_stock_settings()
self.cant_change() self.cant_change()
self.update_show_in_website()
if not self.get("__islocal"): if not self.get("__islocal"):
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
@ -476,6 +477,10 @@ class Item(WebsiteGenerator):
[self.remove(d) for d in to_remove] [self.remove(d) for d in to_remove]
def update_show_in_website(self):
if self.disabled:
self.show_in_website = False
def update_template_tables(self): def update_template_tables(self):
template = frappe.get_doc("Item", self.variant_of) template = frappe.get_doc("Item", self.variant_of)