Merge branch 'develop' of https://github.com/frappe/erpnext into purchase-dashboard
This commit is contained in:
commit
e46916be56
@ -112,8 +112,8 @@ class GLEntry(Document):
|
|||||||
from tabAccount where name=%s""", self.account, as_dict=1)[0]
|
from tabAccount where name=%s""", self.account, as_dict=1)[0]
|
||||||
|
|
||||||
if ret.is_group==1:
|
if ret.is_group==1:
|
||||||
frappe.throw(_("{0} {1}: Account {2} cannot be a Group")
|
frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in
|
||||||
.format(self.voucher_type, self.voucher_no, self.account))
|
transactions''').format(self.voucher_type, self.voucher_no, self.account))
|
||||||
|
|
||||||
if ret.docstatus==2:
|
if ret.docstatus==2:
|
||||||
frappe.throw(_("{0} {1}: Account {2} is inactive")
|
frappe.throw(_("{0} {1}: Account {2} is inactive")
|
||||||
|
|||||||
@ -451,8 +451,6 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
||||||
|
|
||||||
def set_remarks(self):
|
def set_remarks(self):
|
||||||
if self.remarks: return
|
|
||||||
|
|
||||||
if self.payment_type=="Internal Transfer":
|
if self.payment_type=="Internal Transfer":
|
||||||
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
||||||
.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
|
.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
|
||||||
|
|||||||
@ -1020,6 +1020,40 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
# calculate totals again after applying TDS
|
# calculate totals again after applying TDS
|
||||||
self.calculate_taxes_and_totals()
|
self.calculate_taxes_and_totals()
|
||||||
|
|
||||||
|
def set_status(self, update=False, status=None, update_modified=True):
|
||||||
|
if self.is_new():
|
||||||
|
if self.get('amended_from'):
|
||||||
|
self.status = 'Draft'
|
||||||
|
return
|
||||||
|
|
||||||
|
precision = self.precision("outstanding_amount")
|
||||||
|
outstanding_amount = flt(self.outstanding_amount, precision)
|
||||||
|
due_date = getdate(self.due_date)
|
||||||
|
nowdate = getdate()
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
if self.docstatus == 2:
|
||||||
|
status = "Cancelled"
|
||||||
|
elif self.docstatus == 1:
|
||||||
|
if outstanding_amount > 0 and due_date < nowdate:
|
||||||
|
self.status = "Overdue"
|
||||||
|
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||||
|
self.status = "Unpaid"
|
||||||
|
#Check if outstanding amount is 0 due to debit note issued against invoice
|
||||||
|
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||||
|
self.status = "Debit Note Issued"
|
||||||
|
elif self.is_return == 1:
|
||||||
|
self.status = "Return"
|
||||||
|
elif outstanding_amount<=0:
|
||||||
|
self.status = "Paid"
|
||||||
|
else:
|
||||||
|
self.status = "Submitted"
|
||||||
|
else:
|
||||||
|
self.status = "Draft"
|
||||||
|
|
||||||
|
if update:
|
||||||
|
self.db_set('status', self.status, update_modified = update_modified)
|
||||||
|
|
||||||
def get_list_context(context=None):
|
def get_list_context(context=None):
|
||||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||||
|
|||||||
@ -86,6 +86,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
|
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
|
||||||
|
pi_doc.load_from_db()
|
||||||
|
self.assertTrue(pi_doc.status, "Paid")
|
||||||
|
|
||||||
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
|
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
|
||||||
unlink_payment_on_cancel_of_invoice()
|
unlink_payment_on_cancel_of_invoice()
|
||||||
@ -203,7 +205,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
pi.load_from_db()
|
||||||
|
|
||||||
|
self.assertTrue(pi.status, "Unpaid")
|
||||||
self.check_gle_for_pi(pi.name)
|
self.check_gle_for_pi(pi.name)
|
||||||
|
|
||||||
def check_gle_for_pi(self, pi):
|
def check_gle_for_pi(self, pi):
|
||||||
@ -234,6 +238,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
pi = frappe.copy_doc(test_records[0])
|
pi = frappe.copy_doc(test_records[0])
|
||||||
pi.insert()
|
pi.insert()
|
||||||
|
pi.load_from_db()
|
||||||
|
|
||||||
|
self.assertTrue(pi.status, "Draft")
|
||||||
pi.naming_series = 'TEST-'
|
pi.naming_series = 'TEST-'
|
||||||
|
|
||||||
self.assertRaises(frappe.CannotChangeConstantError, pi.save)
|
self.assertRaises(frappe.CannotChangeConstantError, pi.save)
|
||||||
@ -248,6 +255,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pi.get("taxes").pop(1)
|
pi.get("taxes").pop(1)
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.status, "Unpaid")
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||||
@ -599,6 +608,11 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
# return entry
|
# return entry
|
||||||
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
|
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
|
||||||
|
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.status, "Debit Note Issued")
|
||||||
|
pi1.load_from_db()
|
||||||
|
self.assertTrue(pi1.status, "Return")
|
||||||
|
|
||||||
actual_qty_2 = get_qty_after_transaction()
|
actual_qty_2 = get_qty_after_transaction()
|
||||||
self.assertEqual(actual_qty_1 - 2, actual_qty_2)
|
self.assertEqual(actual_qty_1 - 2, actual_qty_2)
|
||||||
|
|
||||||
@ -771,6 +785,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
|
||||||
|
|
||||||
pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.status, "Return")
|
||||||
|
|
||||||
outstanding_amount = get_outstanding_amount(pi.doctype,
|
outstanding_amount = get_outstanding_amount(pi.doctype,
|
||||||
pi.name, "Creditors - _TC", pi.supplier, "Supplier")
|
pi.name, "Creditors - _TC", pi.supplier, "Supplier")
|
||||||
|
|||||||
@ -18,6 +18,10 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
|||||||
refresh: function() {
|
refresh: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
|
if (this.frm.doc.__islocal && !this.frm.doc.valid_till) {
|
||||||
|
this.frm.set_value('valid_till', frappe.datetime.add_months(this.frm.doc.transaction_date, 1));
|
||||||
|
}
|
||||||
if (this.frm.doc.docstatus === 1) {
|
if (this.frm.doc.docstatus === 1) {
|
||||||
cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order,
|
cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order,
|
||||||
__('Create'));
|
__('Create'));
|
||||||
|
|||||||
@ -13,9 +13,10 @@
|
|||||||
"supplier",
|
"supplier",
|
||||||
"supplier_name",
|
"supplier_name",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"transaction_date",
|
|
||||||
"amended_from",
|
|
||||||
"company",
|
"company",
|
||||||
|
"transaction_date",
|
||||||
|
"valid_till",
|
||||||
|
"amended_from",
|
||||||
"address_section",
|
"address_section",
|
||||||
"supplier_address",
|
"supplier_address",
|
||||||
"contact_person",
|
"contact_person",
|
||||||
@ -791,13 +792,18 @@
|
|||||||
"options": "Opportunity",
|
"options": "Opportunity",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "valid_till",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Valid Till"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-shopping-cart",
|
"icon": "fa fa-shopping-cart",
|
||||||
"idx": 29,
|
"idx": 29,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2019-12-30 19:17:28.208693",
|
"modified": "2020-04-15 11:44:52.958022",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Supplier Quotation",
|
"name": "Supplier Quotation",
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt, nowdate, add_days
|
from frappe.utils import flt, nowdate, add_days, getdate
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
from erpnext.controllers.buying_controller import BuyingController
|
from erpnext.controllers.buying_controller import BuyingController
|
||||||
@ -28,6 +28,7 @@ class SupplierQuotation(BuyingController):
|
|||||||
validate_for_items(self)
|
validate_for_items(self)
|
||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
self.validate_uom_is_integer("uom", "qty")
|
self.validate_uom_is_integer("uom", "qty")
|
||||||
|
self.validate_valid_till()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
frappe.db.set(self, "status", "Submitted")
|
frappe.db.set(self, "status", "Submitted")
|
||||||
@ -52,6 +53,11 @@ class SupplierQuotation(BuyingController):
|
|||||||
"is_child_table": True
|
"is_child_table": True
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def validate_valid_till(self):
|
||||||
|
if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date):
|
||||||
|
frappe.throw(_("Valid till Date cannot be before Transaction Date"))
|
||||||
|
|
||||||
def update_rfq_supplier_status(self, include_me):
|
def update_rfq_supplier_status(self, include_me):
|
||||||
rfq_list = set([])
|
rfq_list = set([])
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
@ -158,3 +164,11 @@ def make_quotation(source_name, target_doc=None):
|
|||||||
}, target_doc)
|
}, target_doc)
|
||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
|
def set_expired_status():
|
||||||
|
frappe.db.sql("""
|
||||||
|
UPDATE
|
||||||
|
`tabSupplier Quotation` SET `status` = 'Expired'
|
||||||
|
WHERE
|
||||||
|
`status` not in ('Cancelled', 'Stopped') AND `valid_till` < %s
|
||||||
|
""", (nowdate()))
|
||||||
@ -5,6 +5,8 @@ frappe.listview_settings['Supplier Quotation'] = {
|
|||||||
return [__("Ordered"), "green", "status,=,Ordered"];
|
return [__("Ordered"), "green", "status,=,Ordered"];
|
||||||
} else if(doc.status==="Rejected") {
|
} else if(doc.status==="Rejected") {
|
||||||
return [__("Lost"), "darkgrey", "status,=,Lost"];
|
return [__("Lost"), "darkgrey", "status,=,Lost"];
|
||||||
|
} else if(doc.status==="Expired") {
|
||||||
|
return [__("Expired"), "darkgrey", "status,=,Expired"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,20 +5,18 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
|||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
label: __("Supplier Quotation"),
|
label: __("Company"),
|
||||||
options: "Supplier Quotation",
|
options: "Company",
|
||||||
fieldname: "supplier_quotation",
|
fieldname: "company",
|
||||||
default: "",
|
default: frappe.defaults.get_user_default("Company"),
|
||||||
get_query: () => {
|
"reqd": 1
|
||||||
return { filters: { "docstatus": ["<", 2] } }
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
reqd: 1,
|
reqd: 1,
|
||||||
default: "",
|
default: "",
|
||||||
options: "Item",
|
options: "Item",
|
||||||
label: __("Item"),
|
label: __("Item"),
|
||||||
fieldname: "item",
|
fieldname: "item_code",
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
get_query: () => {
|
get_query: () => {
|
||||||
let quote = frappe.query_report.get_filter_value('supplier_quotation');
|
let quote = frappe.query_report.get_filter_value('supplier_quotation');
|
||||||
@ -37,8 +35,37 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "supplier",
|
||||||
|
label: __("Supplier"),
|
||||||
|
fieldtype: "MultiSelectList",
|
||||||
|
get_data: function(txt) {
|
||||||
|
return frappe.db.get_link_options('Supplier', txt);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Link",
|
||||||
|
label: __("Supplier Quotation"),
|
||||||
|
options: "Supplier Quotation",
|
||||||
|
fieldname: "supplier_quotation",
|
||||||
|
default: "",
|
||||||
|
get_query: () => {
|
||||||
|
return { filters: { "docstatus": ["<", 2] } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Link",
|
||||||
|
label: __("Request for Quotation"),
|
||||||
|
options: "Request for Quotation",
|
||||||
|
fieldname: "request_for_quotation",
|
||||||
|
default: "",
|
||||||
|
get_query: () => {
|
||||||
|
return { filters: { "docstatus": ["<", 2] } }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
onload: (report) => {
|
onload: (report) => {
|
||||||
// Create a button for setting the default supplier
|
// Create a button for setting the default supplier
|
||||||
report.page.add_inner_button(__("Select Default Supplier"), () => {
|
report.page.add_inner_button(__("Select Default Supplier"), () => {
|
||||||
@ -102,6 +129,4 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
|||||||
});
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2,103 +2,180 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from erpnext.setup.utils import get_exchange_rate
|
|
||||||
from frappe.utils import flt, cint
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe.utils import flt, cint
|
||||||
|
from frappe import _
|
||||||
|
from collections import defaultdict
|
||||||
|
from erpnext.setup.utils import get_exchange_rate
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
qty_list = get_quantity_list(filters.item)
|
if not filters:
|
||||||
data = get_quote_list(filters.item, qty_list)
|
return [], []
|
||||||
columns = get_columns(qty_list)
|
|
||||||
return columns, data
|
conditions = get_conditions(filters)
|
||||||
|
supplier_quotation_data = get_data(filters, conditions)
|
||||||
def get_quote_list(item, qty_list):
|
columns = get_columns()
|
||||||
out = []
|
|
||||||
if not item:
|
data, chart_data = prepare_data(supplier_quotation_data)
|
||||||
|
|
||||||
|
return columns, data, None, chart_data
|
||||||
|
|
||||||
|
def get_conditions(filters):
|
||||||
|
conditions = ""
|
||||||
|
if filters.get("supplier_quotation"):
|
||||||
|
conditions += " AND sqi.parent = %(supplier_quotation)s"
|
||||||
|
|
||||||
|
if filters.get("request_for_quotation"):
|
||||||
|
conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s"
|
||||||
|
|
||||||
|
if filters.get("supplier"):
|
||||||
|
conditions += " AND sq.supplier in %(supplier)s"
|
||||||
|
return conditions
|
||||||
|
|
||||||
|
def get_data(filters, conditions):
|
||||||
|
if not filters.get("item_code"):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
suppliers = []
|
supplier_quotation_data = frappe.db.sql("""SELECT
|
||||||
price_data = []
|
sqi.parent, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
|
||||||
company_currency = frappe.db.get_default("currency")
|
sq.supplier
|
||||||
float_precision = cint(frappe.db.get_default("float_precision")) or 2
|
FROM
|
||||||
# Get the list of suppliers
|
`tabSupplier Quotation Item` sqi,
|
||||||
for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item`
|
`tabSupplier Quotation` sq
|
||||||
where item_code=%s and docstatus < 2""", item, as_dict=1):
|
WHERE
|
||||||
for splr in frappe.db.sql("""select supplier from `tabSupplier Quotation`
|
sqi.item_code = %(item_code)s
|
||||||
where name =%s and docstatus < 2""", root.parent, as_dict=1):
|
AND sqi.parent = sq.name
|
||||||
ip = frappe._dict({
|
AND sqi.docstatus < 2
|
||||||
"supplier": splr.supplier,
|
AND sq.company = %(company)s
|
||||||
"qty": root.qty,
|
AND sq.status != 'Expired'
|
||||||
"parent": root.parent,
|
{0}""".format(conditions), filters, as_dict=1)
|
||||||
"rate": root.rate
|
|
||||||
})
|
return supplier_quotation_data
|
||||||
price_data.append(ip)
|
|
||||||
suppliers.append(splr.supplier)
|
def prepare_data(supplier_quotation_data):
|
||||||
|
out, suppliers, qty_list = [], [], []
|
||||||
|
supplier_wise_map = defaultdict(list)
|
||||||
|
supplier_qty_price_map = {}
|
||||||
|
|
||||||
|
company_currency = frappe.db.get_default("currency")
|
||||||
|
float_precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||||
|
|
||||||
|
for data in supplier_quotation_data:
|
||||||
|
supplier = data.get("supplier")
|
||||||
|
supplier_currency = frappe.db.get_value("Supplier", data.get("supplier"), "default_currency")
|
||||||
|
|
||||||
#Add a row for each supplier
|
|
||||||
for root in set(suppliers):
|
|
||||||
supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
|
|
||||||
if supplier_currency:
|
if supplier_currency:
|
||||||
exchange_rate = get_exchange_rate(supplier_currency, company_currency)
|
exchange_rate = get_exchange_rate(supplier_currency, company_currency)
|
||||||
else:
|
else:
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
|
|
||||||
row = frappe._dict({
|
row = {
|
||||||
"supplier_name": root
|
"quotation": data.get("parent"),
|
||||||
})
|
"qty": data.get("qty"),
|
||||||
for col in qty_list:
|
"price": flt(data.get("rate") * exchange_rate, float_precision),
|
||||||
# Get the quantity for this row
|
"uom": data.get("uom"),
|
||||||
for item_price in price_data:
|
"request_for_quotation": data.get("request_for_quotation"),
|
||||||
if str(item_price.qty) == col.key and item_price.supplier == root:
|
}
|
||||||
row[col.key] = flt(item_price.rate * exchange_rate, float_precision)
|
|
||||||
row[col.key + "QUOTE"] = item_price.parent
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
row[col.key] = ""
|
|
||||||
row[col.key + "QUOTE"] = ""
|
|
||||||
out.append(row)
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
def get_quantity_list(item):
|
|
||||||
out = []
|
|
||||||
|
|
||||||
if item:
|
|
||||||
qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item`
|
|
||||||
where ifnull(item_code,'')=%s and docstatus < 2 order by qty""", item, as_dict=1)
|
|
||||||
|
|
||||||
for qt in qty_list:
|
# map for report view of form {'supplier1':[{},{},...]}
|
||||||
col = frappe._dict({
|
supplier_wise_map[supplier].append(row)
|
||||||
"key": str(qt.qty),
|
|
||||||
"label": "Qty: " + str(int(qt.qty))
|
|
||||||
})
|
|
||||||
out.append(col)
|
|
||||||
|
|
||||||
return out
|
# map for chart preparation of the form {'supplier1': {'qty': 'price'}}
|
||||||
|
if not supplier in supplier_qty_price_map:
|
||||||
def get_columns(qty_list):
|
supplier_qty_price_map[supplier] = {}
|
||||||
|
supplier_qty_price_map[supplier][row["qty"]] = row["price"]
|
||||||
|
|
||||||
|
suppliers.append(supplier)
|
||||||
|
qty_list.append(data.get("qty"))
|
||||||
|
|
||||||
|
suppliers = list(set(suppliers))
|
||||||
|
qty_list = list(set(qty_list))
|
||||||
|
|
||||||
|
# final data format for report view
|
||||||
|
for supplier in suppliers:
|
||||||
|
supplier_wise_map[supplier][0].update({"supplier_name": supplier})
|
||||||
|
for entry in supplier_wise_map[supplier]:
|
||||||
|
out.append(entry)
|
||||||
|
|
||||||
|
chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
|
||||||
|
|
||||||
|
return out, chart_data
|
||||||
|
|
||||||
|
def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map):
|
||||||
|
data_points_map = {}
|
||||||
|
qty_list.sort()
|
||||||
|
|
||||||
|
# create qty wise values map of the form {'qty1':[value1, value2]}
|
||||||
|
for supplier in suppliers:
|
||||||
|
entry = supplier_qty_price_map[supplier]
|
||||||
|
for qty in qty_list:
|
||||||
|
if not qty in data_points_map:
|
||||||
|
data_points_map[qty] = []
|
||||||
|
if qty in entry:
|
||||||
|
data_points_map[qty].append(entry[qty])
|
||||||
|
else:
|
||||||
|
data_points_map[qty].append(None)
|
||||||
|
|
||||||
|
dataset = []
|
||||||
|
for qty in qty_list:
|
||||||
|
datapoints = {
|
||||||
|
"name": _("Price for Qty ") + str(qty),
|
||||||
|
"values": data_points_map[qty]
|
||||||
|
}
|
||||||
|
dataset.append(datapoints)
|
||||||
|
|
||||||
|
chart_data = {
|
||||||
|
"data": {
|
||||||
|
"labels": suppliers,
|
||||||
|
"datasets": dataset
|
||||||
|
},
|
||||||
|
"type": "bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
return chart_data
|
||||||
|
|
||||||
|
def get_columns():
|
||||||
columns = [{
|
columns = [{
|
||||||
"fieldname": "supplier_name",
|
"fieldname": "supplier_name",
|
||||||
"label": "Supplier",
|
"label": _("Supplier"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier",
|
"options": "Supplier",
|
||||||
"width": 200
|
"width": 200
|
||||||
}]
|
},
|
||||||
|
{
|
||||||
for qty in qty_list:
|
"fieldname": "quotation",
|
||||||
columns.append({
|
"label": _("Supplier Quotation"),
|
||||||
"fieldname": qty.key,
|
"fieldtype": "Link",
|
||||||
"label": qty.label,
|
"options": "Supplier Quotation",
|
||||||
"fieldtype": "Currency",
|
"width": 200
|
||||||
"options": "currency",
|
},
|
||||||
"width": 80
|
{
|
||||||
})
|
"fieldname": "qty",
|
||||||
columns.append({
|
"label": _("Quantity"),
|
||||||
"fieldname": qty.key + "QUOTE",
|
"fieldtype": "Float",
|
||||||
"label": "Quotation",
|
"width": 80
|
||||||
"fieldtype": "Link",
|
},
|
||||||
"options": "Supplier Quotation",
|
{
|
||||||
"width": 90
|
"fieldname": "price",
|
||||||
})
|
"label": _("Price"),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"width": 110
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "uom",
|
||||||
|
"label": _("UOM"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "UOM",
|
||||||
|
"width": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "request_for_quotation",
|
||||||
|
"label": _("Request for Quotation"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Request for Quotation",
|
||||||
|
"width": 200
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
@ -69,17 +69,6 @@ status_map = {
|
|||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
["Closed", "eval:self.status=='Closed'"],
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
],
|
],
|
||||||
"Purchase Invoice": [
|
|
||||||
["Draft", None],
|
|
||||||
["Submitted", "eval:self.docstatus==1"],
|
|
||||||
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
|
|
||||||
["Return", "eval:self.is_return==1 and self.docstatus==1"],
|
|
||||||
["Debit Note Issued",
|
|
||||||
"eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
|
|
||||||
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
|
|
||||||
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
|
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
|
||||||
],
|
|
||||||
"Material Request": [
|
"Material Request": [
|
||||||
["Draft", None],
|
["Draft", None],
|
||||||
["Stopped", "eval:self.status == 'Stopped'"],
|
["Stopped", "eval:self.status == 'Stopped'"],
|
||||||
|
|||||||
@ -308,7 +308,8 @@ scheduler_events = {
|
|||||||
"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts",
|
"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts",
|
||||||
"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status",
|
"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status",
|
||||||
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
|
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
|
||||||
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status"
|
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status",
|
||||||
|
"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status"
|
||||||
],
|
],
|
||||||
"daily_long": [
|
"daily_long": [
|
||||||
"erpnext.setup.doctype.email_digest.email_digest.send",
|
"erpnext.setup.doctype.email_digest.email_digest.send",
|
||||||
|
|||||||
@ -76,25 +76,15 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-19 18:06:45.361830",
|
"modified": "2020-05-14 17:17:38.883126",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee Other Income",
|
"name": "Employee Other Income",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"create": 1,
|
"amend": 1,
|
||||||
"delete": 1,
|
"cancel": 1,
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"share": 1,
|
|
||||||
"write": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@ -104,9 +94,12 @@
|
|||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "HR Manager",
|
"role": "HR Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 1,
|
||||||
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@ -116,9 +109,12 @@
|
|||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "HR User",
|
"role": "HR User",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 1,
|
||||||
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@ -128,6 +124,7 @@
|
|||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Employee",
|
"role": "Employee",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -683,3 +683,4 @@ erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashme
|
|||||||
execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
|
execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
|
||||||
execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
|
execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
|
||||||
erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
|
erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
|
||||||
|
erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("buying", "doctype", "supplier_quotation")
|
||||||
|
frappe.db.sql("""UPDATE `tabSupplier Quotation`
|
||||||
|
SET valid_till = DATE_ADD(transaction_date , INTERVAL 1 MONTH)
|
||||||
|
WHERE docstatus < 2""")
|
||||||
@ -251,8 +251,7 @@ def get_tax_template_for_sez(party_details, master_doctype, company, party_type)
|
|||||||
|
|
||||||
|
|
||||||
def calculate_annual_eligible_hra_exemption(doc):
|
def calculate_annual_eligible_hra_exemption(doc):
|
||||||
basic_component = frappe.get_cached_value('Company', doc.company, "basic_component")
|
basic_component, hra_component = frappe.db.get_value('Company', doc.company, ["basic_component", "hra_component"])
|
||||||
hra_component = frappe.get_cached_value('Company', doc.company, "hra_component")
|
|
||||||
if not (basic_component and hra_component):
|
if not (basic_component and hra_component):
|
||||||
frappe.throw(_("Please mention Basic and HRA component in Company"))
|
frappe.throw(_("Please mention Basic and HRA component in Company"))
|
||||||
annual_exemption, monthly_exemption, hra_amount = 0, 0, 0
|
annual_exemption, monthly_exemption, hra_amount = 0, 0, 0
|
||||||
|
|||||||
@ -10,14 +10,16 @@ from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings
|
|||||||
from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status
|
from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def get_product_info_for_website(item_code):
|
def get_product_info_for_website(item_code, skip_quotation_creation=False):
|
||||||
"""get product price / stock info for website"""
|
"""get product price / stock info for website"""
|
||||||
|
|
||||||
cart_settings = get_shopping_cart_settings()
|
cart_settings = get_shopping_cart_settings()
|
||||||
if not cart_settings.enabled:
|
if not cart_settings.enabled:
|
||||||
return frappe._dict()
|
return frappe._dict()
|
||||||
|
|
||||||
cart_quotation = _get_cart_quotation()
|
cart_quotation = frappe._dict()
|
||||||
|
if not skip_quotation_creation:
|
||||||
|
cart_quotation = _get_cart_quotation()
|
||||||
|
|
||||||
price = get_price(
|
price = get_price(
|
||||||
item_code,
|
item_code,
|
||||||
@ -51,7 +53,7 @@ def get_product_info_for_website(item_code):
|
|||||||
|
|
||||||
def set_product_info_for_website(item):
|
def set_product_info_for_website(item):
|
||||||
"""set product price uom for website"""
|
"""set product price uom for website"""
|
||||||
product_info = get_product_info_for_website(item.item_code)
|
product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True)
|
||||||
|
|
||||||
if product_info:
|
if product_info:
|
||||||
item.update(product_info)
|
item.update(product_info)
|
||||||
|
|||||||
@ -467,7 +467,7 @@ class Item(WebsiteGenerator):
|
|||||||
|
|
||||||
def set_shopping_cart_data(self, context):
|
def set_shopping_cart_data(self, context):
|
||||||
from erpnext.shopping_cart.product_info import get_product_info_for_website
|
from erpnext.shopping_cart.product_info import get_product_info_for_website
|
||||||
context.shopping_cart = get_product_info_for_website(self.name)
|
context.shopping_cart = get_product_info_for_website(self.name, skip_quotation_creation=True)
|
||||||
|
|
||||||
def add_default_uom_in_conversion_factor_table(self):
|
def add_default_uom_in_conversion_factor_table(self):
|
||||||
uom_conv_list = [d.uom for d in self.get("uoms")]
|
uom_conv_list = [d.uom for d in self.get("uoms")]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user