diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
index ee73ccaa61..5278d8b241 100644
--- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
@@ -195,88 +195,91 @@ def create_sales_invoice_record(qty=1):
def create_records():
# create a new loyalty Account
- if frappe.db.exists("Account", "Loyalty - _TC"):
- return
-
- frappe.get_doc({
- "doctype": "Account",
- "account_name": "Loyalty",
- "parent_account": "Direct Expenses - _TC",
- "company": "_Test Company",
- "is_group": 0,
- "account_type": "Expense Account",
- }).insert()
+ if not frappe.db.exists("Account", "Loyalty - _TC"):
+ frappe.get_doc({
+ "doctype": "Account",
+ "account_name": "Loyalty",
+ "parent_account": "Direct Expenses - _TC",
+ "company": "_Test Company",
+ "is_group": 0,
+ "account_type": "Expense Account",
+ }).insert()
# create a new loyalty program Single tier
- frappe.get_doc({
- "doctype": "Loyalty Program",
- "loyalty_program_name": "Test Single Loyalty",
- "auto_opt_in": 1,
- "from_date": today(),
- "loyalty_program_type": "Single Tier Program",
- "conversion_factor": 1,
- "expiry_duration": 10,
- "company": "_Test Company",
- "cost_center": "Main - _TC",
- "expense_account": "Loyalty - _TC",
- "collection_rules": [{
- 'tier_name': 'Silver',
- 'collection_factor': 1000,
- 'min_spent': 1000
- }]
- }).insert()
-
- # create a new customer
- frappe.get_doc({
- "customer_group": "_Test Customer Group",
- "customer_name": "Test Loyalty Customer",
- "customer_type": "Individual",
- "doctype": "Customer",
- "territory": "_Test Territory"
- }).insert()
-
- # create a new loyalty program Multiple tier
- frappe.get_doc({
- "doctype": "Loyalty Program",
- "loyalty_program_name": "Test Multiple Loyalty",
- "auto_opt_in": 1,
- "from_date": today(),
- "loyalty_program_type": "Multiple Tier Program",
- "conversion_factor": 1,
- "expiry_duration": 10,
- "company": "_Test Company",
- "cost_center": "Main - _TC",
- "expense_account": "Loyalty - _TC",
- "collection_rules": [
- {
+ if not frappe.db.exists("Loyalty Program","Test Single Loyalty"):
+ frappe.get_doc({
+ "doctype": "Loyalty Program",
+ "loyalty_program_name": "Test Single Loyalty",
+ "auto_opt_in": 1,
+ "from_date": today(),
+ "loyalty_program_type": "Single Tier Program",
+ "conversion_factor": 1,
+ "expiry_duration": 10,
+ "company": "_Test Company",
+ "cost_center": "Main - _TC",
+ "expense_account": "Loyalty - _TC",
+ "collection_rules": [{
'tier_name': 'Silver',
'collection_factor': 1000,
- 'min_spent': 10000
- },
- {
- 'tier_name': 'Gold',
- 'collection_factor': 1000,
- 'min_spent': 19000
- }
- ]
- }).insert()
+ 'min_spent': 1000
+ }]
+ }).insert()
+
+ # create a new customer
+ if not frappe.db.exists("Customer","Test Loyalty Customer"):
+ frappe.get_doc({
+ "customer_group": "_Test Customer Group",
+ "customer_name": "Test Loyalty Customer",
+ "customer_type": "Individual",
+ "doctype": "Customer",
+ "territory": "_Test Territory"
+ }).insert()
+
+ # create a new loyalty program Multiple tier
+ if not frappe.db.exists("Loyalty Program","Test Multiple Loyalty"):
+ frappe.get_doc({
+ "doctype": "Loyalty Program",
+ "loyalty_program_name": "Test Multiple Loyalty",
+ "auto_opt_in": 1,
+ "from_date": today(),
+ "loyalty_program_type": "Multiple Tier Program",
+ "conversion_factor": 1,
+ "expiry_duration": 10,
+ "company": "_Test Company",
+ "cost_center": "Main - _TC",
+ "expense_account": "Loyalty - _TC",
+ "collection_rules": [
+ {
+ 'tier_name': 'Silver',
+ 'collection_factor': 1000,
+ 'min_spent': 10000
+ },
+ {
+ 'tier_name': 'Gold',
+ 'collection_factor': 1000,
+ 'min_spent': 19000
+ }
+ ]
+ }).insert()
# create an item
- item = frappe.get_doc({
- "doctype": "Item",
- "item_code": "Loyal Item",
- "item_name": "Loyal Item",
- "item_group": "All Item Groups",
- "company": "_Test Company",
- "is_stock_item": 1,
- "opening_stock": 100,
- "valuation_rate": 10000,
- }).insert()
+ if not frappe.db.exists("Item", "Loyal Item"):
+ frappe.get_doc({
+ "doctype": "Item",
+ "item_code": "Loyal Item",
+ "item_name": "Loyal Item",
+ "item_group": "All Item Groups",
+ "company": "_Test Company",
+ "is_stock_item": 1,
+ "opening_stock": 100,
+ "valuation_rate": 10000,
+ }).insert()
# create item price
- frappe.get_doc({
- "doctype": "Item Price",
- "price_list": "Standard Selling",
- "item_code": item.item_code,
- "price_list_rate": 10000
- }).insert()
+ if not frappe.db.exists("Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}):
+ frappe.get_doc({
+ "doctype": "Item Price",
+ "price_list": "Standard Selling",
+ "item_code": "Loyal Item",
+ "price_list_rate": 10000
+ }).insert()
diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
index aa6a388df5..8de54d5bde 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
@@ -45,7 +45,7 @@ class TestPOSClosingEntry(unittest.TestCase):
frappe.set_user("Administrator")
frappe.db.sql("delete from `tabPOS Profile`")
-def init_user_and_profile():
+def init_user_and_profile(**args):
user = 'test@example.com'
test_user = frappe.get_doc('User', user)
@@ -53,7 +53,7 @@ def init_user_and_profile():
test_user.add_roles(*roles)
frappe.set_user(user)
- pos_profile = make_pos_profile()
+ pos_profile = make_pos_profile(**args)
pos_profile.append('applicable_for_users', {
'default': 1,
'user': user
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index ba68df7673..1669ca4094 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -139,7 +139,8 @@ class POSInvoice(SalesInvoice):
frappe.throw(_("At least one mode of payment is required for POS invoice."))
def validate_change_account(self):
- if frappe.db.get_value("Account", self.account_for_change_amount, "company") != self.company:
+ if self.change_amount and self.account_for_change_amount and \
+ frappe.db.get_value("Account", self.account_for_change_amount, "company") != self.company:
frappe.throw(_("The selected change account {} doesn't belongs to Company {}.").format(self.account_for_change_amount, self.company))
def validate_change_amount(self):
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 1a5920d8ab..c179360b01 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -7,6 +7,8 @@ import frappe
import unittest, copy, time
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
class TestPOSInvoice(unittest.TestCase):
def test_timestamp_change(self):
@@ -221,29 +223,29 @@ class TestPOSInvoice(unittest.TestCase):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
- se = make_serialized_item(company='_Test Company with perpetual inventory',
- target_warehouse="Stores - TCP1", cost_center='Main - TCP1', expense_account='Cost of Goods Sold - TCP1')
+ se = make_serialized_item(company='_Test Company',
+ target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
- pos = create_pos_invoice(company='_Test Company with perpetual inventory', debit_to='Debtors - TCP1',
- account_for_change_amount='Cash - TCP1', warehouse='Stores - TCP1', income_account='Sales - TCP1',
- expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1',
+ pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
+ account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
+ expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
pos.get("items")[0].serial_no = serial_nos[0]
- pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 1000})
+ pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
pos.insert()
pos.submit()
- pos2 = create_pos_invoice(company='_Test Company with perpetual inventory', debit_to='Debtors - TCP1',
- account_for_change_amount='Cash - TCP1', warehouse='Stores - TCP1', income_account='Sales - TCP1',
- expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1',
+ pos2 = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
+ account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
+ expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
pos2.get("items")[0].serial_no = serial_nos[0]
- pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 1000})
+ pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
self.assertRaises(frappe.ValidationError, pos2.insert)
@@ -285,7 +287,7 @@ class TestPOSInvoice(unittest.TestCase):
after_redeem_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program)
self.assertEqual(after_redeem_lp_details.loyalty_points, 9)
-
+
def test_merging_into_sales_invoice_with_discount(self):
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
@@ -294,7 +296,7 @@ class TestPOSInvoice(unittest.TestCase):
test_user, pos_profile = init_user_and_profile()
pos_inv = create_pos_invoice(rate=300, additional_discount_percentage=10, do_not_submit=1)
pos_inv.append('payments', {
- 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
+ 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 270
})
pos_inv.submit()
@@ -307,9 +309,10 @@ class TestPOSInvoice(unittest.TestCase):
merge_pos_invoices()
pos_inv.load_from_db()
- sales_invoice = frappe.get_doc("Sales Invoice", pos_inv.consolidated_invoice)
- self.assertEqual(sales_invoice.grand_total, 3500)
-
+ rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
+ self.assertEqual(rounded_total, 3470)
+ frappe.set_user("Administrator")
+
def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
@@ -348,8 +351,55 @@ class TestPOSInvoice(unittest.TestCase):
merge_pos_invoices()
pos_inv.load_from_db()
- sales_invoice = frappe.get_doc("Sales Invoice", pos_inv.consolidated_invoice)
- self.assertEqual(sales_invoice.rounded_total, 840)
+ rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
+ self.assertEqual(rounded_total, 840)
+ frappe.set_user("Administrator")
+
+ def test_merging_with_validate_selling_price(self):
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+ from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
+
+ if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
+ frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
+
+ make_purchase_receipt(item_code="_Test Item", warehouse="_Test Warehouse - _TC", qty=1, rate=300)
+ frappe.db.sql("delete from `tabPOS Invoice`")
+ test_user, pos_profile = init_user_and_profile()
+ pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
+ pos_inv.append('payments', {
+ 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
+ })
+ pos_inv.append('taxes', {
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Service Tax - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Service Tax",
+ "rate": 14,
+ 'included_in_print_rate': 1
+ })
+ self.assertRaises(frappe.ValidationError, pos_inv.submit)
+
+ pos_inv2 = create_pos_invoice(rate=400, do_not_submit=1)
+ pos_inv2.append('payments', {
+ 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400
+ })
+ pos_inv2.append('taxes', {
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Service Tax - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Service Tax",
+ "rate": 14,
+ 'included_in_print_rate': 1
+ })
+ pos_inv2.submit()
+
+ merge_pos_invoices()
+
+ pos_inv2.load_from_db()
+ rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
+ self.assertEqual(rounded_total, 400)
+ frappe.set_user("Administrator")
+ frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 0)
def create_pos_invoice(**args):
args = frappe._dict(args)
@@ -364,8 +414,6 @@ def create_pos_invoice(**args):
pos_inv.is_pos = 1
pos_inv.pos_profile = args.pos_profile or pos_profile.name
- pos_inv.set_missing_values()
-
if args.posting_date:
pos_inv.set_posting_time = 1
pos_inv.posting_date = args.posting_date or frappe.utils.nowdate()
@@ -379,6 +427,8 @@ def create_pos_invoice(**args):
pos_inv.conversion_rate = args.conversion_rate or 1
pos_inv.account_for_change_amount = args.account_for_change_amount or "Cash - _TC"
+ pos_inv.set_missing_values()
+
pos_inv.append("items", {
"item_code": args.item or args.item_code or "_Test Item",
"warehouse": args.warehouse or "_Test Warehouse - _TC",
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js
index 8ec6a53626..558e21c13a 100755
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.js
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js
@@ -15,15 +15,6 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
erpnext.queries.setup_queries(frm, "Warehouse", function() {
return erpnext.queries.warehouse(frm.doc);
});
-
- frm.call({
- method: "erpnext.accounts.doctype.pos_profile.pos_profile.get_series",
- callback: function(r) {
- if(!r.exc) {
- set_field_options("naming_series", r.message);
- }
- }
- });
});
frappe.ui.form.on('POS Profile', {
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index d4c1791789..999da75997 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -8,7 +8,6 @@
"field_order": [
"disabled",
"section_break_2",
- "naming_series",
"customer",
"company",
"country",
@@ -59,17 +58,6 @@
"fieldname": "section_break_2",
"fieldtype": "Section Break"
},
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Series",
- "no_copy": 1,
- "oldfieldname": "naming_series",
- "oldfieldtype": "Select",
- "options": "[Select]",
- "reqd": 1
- },
{
"fieldname": "customer",
"fieldtype": "Link",
@@ -323,7 +311,7 @@
"icon": "icon-cog",
"idx": 1,
"links": [],
- "modified": "2020-06-29 12:20:30.977272",
+ "modified": "2020-10-01 17:29:27.759088",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",
@@ -350,4 +338,4 @@
],
"sort_field": "modified",
"sort_order": "DESC"
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index 1386b70f55..1d160a5aa7 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -109,10 +109,6 @@ def get_child_nodes(group_type, root):
return frappe.db.sql(""" Select name, lft, rgt from `tab{tab}` where
lft >= {lft} and rgt <= {rgt} order by lft""".format(tab=group_type, lft=lft, rgt=rgt), as_dict=1)
-@frappe.whitelist()
-def get_series():
- return frappe.get_meta("POS Invoice").get_field("naming_series").options or "s"
-
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 079f599706..c5260a1239 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -711,7 +711,8 @@ class PurchaseInvoice(BuyingController):
item.item_tax_amount / self.conversion_rate)
}, item=item))
else:
- cwip_account = get_asset_account("capital_work_in_progress_account", company = self.company)
+ cwip_account = get_asset_account("capital_work_in_progress_account",
+ asset_category=item.asset_category,company=self.company)
cwip_account_currency = get_account_currency(cwip_account)
gl_entries.append(self.get_gl_dict({
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 9a666bf9f8..2e5a7142a3 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1002,7 +1002,8 @@ def make_purchase_invoice(**args):
"cost_center": args.cost_center or "_Test Cost Center - _TC",
"project": args.project,
"rejected_warehouse": args.rejected_warehouse or "",
- "rejected_serial_no": args.rejected_serial_no or ""
+ "rejected_serial_no": args.rejected_serial_no or "",
+ "asset_location": args.location or ""
})
if args.get_taxes_and_charges:
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index cd66f7a086..ae40153890 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -19,6 +19,7 @@
"is_return",
"column_break1",
"company",
+ "company_tax_id",
"posting_date",
"posting_time",
"set_posting_time",
@@ -1926,6 +1927,7 @@
},
{
"default": "0",
+ "depends_on": "eval:(doc.is_pos && doc.is_consolidated)",
"fieldname": "is_consolidated",
"fieldtype": "Check",
"label": "Is Consolidated",
@@ -1940,6 +1942,13 @@
"hide_seconds": 1,
"label": "Is Internal Customer",
"read_only": 1
+ },
+ {
+ "fetch_from": "company.tax_id",
+ "fieldname": "company_tax_id",
+ "fieldtype": "Data",
+ "label": "Company Tax ID",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 92e49d59da..801e688deb 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -428,7 +428,7 @@ class SalesInvoice(SellingController):
if pos.get('account_for_change_amount'):
self.account_for_change_amount = pos.get('account_for_change_amount')
- for fieldname in ('naming_series', 'currency', 'letter_head', 'tc_name',
+ for fieldname in ('currency', 'letter_head', 'tc_name',
'company', 'select_print_heading', 'write_off_account', 'taxes_and_charges',
'write_off_cost_center', 'apply_discount_on', 'cost_center'):
if (not for_validate) or (for_validate and not self.get(fieldname)):
@@ -572,7 +572,8 @@ class SalesInvoice(SellingController):
def validate_pos(self):
if self.is_return:
- if flt(self.paid_amount) + flt(self.write_off_amount) - flt(self.grand_total) > \
+ invoice_total = self.rounded_total or self.grand_total
+ if flt(self.paid_amount) + flt(self.write_off_amount) - flt(invoice_total) > \
1.0/(10.0**(self.precision("grand_total") + 1.0)):
frappe.throw(_("Paid amount + Write Off Amount can not be greater than Grand Total"))
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 07525317aa..552a5d476b 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -345,13 +345,14 @@ class Subscription(Document):
invoice.set_taxes()
# Due date
- invoice.append(
- 'payment_schedule',
- {
- 'due_date': add_days(invoice.posting_date, cint(self.days_until_due)),
- 'invoice_portion': 100
- }
- )
+ if self.days_until_due:
+ invoice.append(
+ 'payment_schedule',
+ {
+ 'due_date': add_days(invoice.posting_date, cint(self.days_until_due)),
+ 'invoice_portion': 100
+ }
+ )
# Discounts
if self.additional_discount_percentage:
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 83d7967f79..8b5e68b359 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -106,6 +106,7 @@ def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_detai
from `tabGL Entry`
where company = %s and
party in %s and fiscal_year=%s and credit > 0
+ and is_opening = 'No'
""", (company, tuple(suppliers), fiscal_year), as_dict=1)
vouchers = [d.voucher_no for d in entries]
@@ -192,6 +193,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No
select distinct voucher_no
from `tabGL Entry`
where party in %s and %s and debit > 0
+ and is_opening = 'No'
""", (tuple(suppliers), condition)) or []
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js
index 57fe4b05be..8f028496cd 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js
@@ -3,6 +3,14 @@
frappe.query_reports["Bank Reconciliation Statement"] = {
"filters": [
+ {
+ "fieldname":"company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "options": "Company",
+ "reqd": 1,
+ "default": frappe.defaults.get_user_default("Company")
+ },
{
"fieldname":"account",
"label": __("Bank Account"),
@@ -12,11 +20,14 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "",
"reqd": 1,
"get_query": function() {
+ var company = frappe.query_report.get_filter_value('company')
return {
"query": "erpnext.controllers.queries.get_account_list",
"filters": [
['Account', 'account_type', 'in', 'Bank, Cash'],
['Account', 'is_group', '=', 0],
+ ['Account', 'disabled', '=', 0],
+ ['Account', 'company', '=', company],
]
}
}
@@ -34,4 +45,4 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
"fieldtype": "Check"
},
]
-}
\ No newline at end of file
+}
diff --git a/erpnext/buying/report/quoted_item_comparison/__init__.py b/erpnext/accounts/report/pos_register/__init__.py
similarity index 100%
rename from erpnext/buying/report/quoted_item_comparison/__init__.py
rename to erpnext/accounts/report/pos_register/__init__.py
diff --git a/erpnext/accounts/report/pos_register/pos_register.js b/erpnext/accounts/report/pos_register/pos_register.js
new file mode 100644
index 0000000000..b8d48d92de
--- /dev/null
+++ b/erpnext/accounts/report/pos_register/pos_register.js
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["POS Register"] = {
+ "filters": [
+ {
+ "fieldname":"company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "options": "Company",
+ "default": frappe.defaults.get_user_default("Company"),
+ "reqd": 1
+ },
+ {
+ "fieldname":"from_date",
+ "label": __("From Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+ "reqd": 1,
+ "width": "60px"
+ },
+ {
+ "fieldname":"to_date",
+ "label": __("To Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.get_today(),
+ "reqd": 1,
+ "width": "60px"
+ },
+ {
+ "fieldname":"pos_profile",
+ "label": __("POS Profile"),
+ "fieldtype": "Link",
+ "options": "POS Profile"
+ },
+ {
+ "fieldname":"cashier",
+ "label": __("Cashier"),
+ "fieldtype": "Link",
+ "options": "User"
+ },
+ {
+ "fieldname":"customer",
+ "label": __("Customer"),
+ "fieldtype": "Link",
+ "options": "Customer"
+ },
+ {
+ "fieldname":"mode_of_payment",
+ "label": __("Payment Method"),
+ "fieldtype": "Link",
+ "options": "Mode of Payment"
+ },
+ {
+ "fieldname":"group_by",
+ "label": __("Group by"),
+ "fieldtype": "Select",
+ "options": ["", "POS Profile", "Cashier", "Payment Method", "Customer"],
+ "default": "POS Profile"
+ },
+ {
+ "fieldname":"is_return",
+ "label": __("Is Return"),
+ "fieldtype": "Check"
+ },
+ ],
+ "formatter": function(value, row, column, data, default_formatter) {
+ value = default_formatter(value, row, column, data);
+ if (data && data.bold) {
+ value = value.bold();
+
+ }
+ return value;
+ }
+};
diff --git a/erpnext/accounts/report/pos_register/pos_register.json b/erpnext/accounts/report/pos_register/pos_register.json
new file mode 100644
index 0000000000..2398b10475
--- /dev/null
+++ b/erpnext/accounts/report/pos_register/pos_register.json
@@ -0,0 +1,30 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2020-09-10 19:25:03.766871",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "modified": "2020-09-10 19:25:15.851331",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "POS Register",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "POS Invoice",
+ "report_name": "POS Register",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Accounts Manager"
+ },
+ {
+ "role": "Accounts User"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py
new file mode 100644
index 0000000000..0bcde64f7f
--- /dev/null
+++ b/erpnext/accounts/report/pos_register/pos_register.py
@@ -0,0 +1,222 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _, _dict
+from erpnext import get_company_currency, get_default_company
+from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
+
+def execute(filters=None):
+ if not filters:
+ return [], []
+
+ validate_filters(filters)
+
+ columns = get_columns(filters)
+
+ group_by_field = get_group_by_field(filters.get("group_by"))
+
+ pos_entries = get_pos_entries(filters, group_by_field)
+ if group_by_field != "mode_of_payment":
+ concat_mode_of_payments(pos_entries)
+
+ # return only entries if group by is unselected
+ if not group_by_field:
+ return columns, pos_entries
+
+ # handle grouping
+ invoice_map, grouped_data = {}, []
+ for d in pos_entries:
+ invoice_map.setdefault(d[group_by_field], []).append(d)
+
+ for key in invoice_map:
+ invoices = invoice_map[key]
+ grouped_data += invoices
+ add_subtotal_row(grouped_data, invoices, group_by_field, key)
+
+ # move group by column to first position
+ column_index = next((index for (index, d) in enumerate(columns) if d["fieldname"] == group_by_field), None)
+ columns.insert(0, columns.pop(column_index))
+
+ return columns, grouped_data
+
+def get_pos_entries(filters, group_by_field):
+ conditions = get_conditions(filters)
+ order_by = "p.posting_date"
+ select_mop_field, from_sales_invoice_payment, group_by_mop_condition = "", "", ""
+ if group_by_field == "mode_of_payment":
+ select_mop_field = ", sip.mode_of_payment"
+ from_sales_invoice_payment = ", `tabSales Invoice Payment` sip"
+ group_by_mop_condition = "sip.parent = p.name AND ifnull(sip.base_amount, 0) != 0 AND"
+ order_by += ", sip.mode_of_payment"
+
+ elif group_by_field:
+ order_by += ", p.{}".format(group_by_field)
+
+ return frappe.db.sql(
+ """
+ SELECT
+ p.posting_date, p.name as pos_invoice, p.pos_profile,
+ p.owner, p.base_grand_total as grand_total, p.base_paid_amount as paid_amount,
+ p.customer, p.is_return {select_mop_field}
+ FROM
+ `tabPOS Invoice` p {from_sales_invoice_payment}
+ WHERE
+ {group_by_mop_condition}
+ {conditions}
+ ORDER BY
+ {order_by}
+ """.format(
+ select_mop_field=select_mop_field,
+ from_sales_invoice_payment=from_sales_invoice_payment,
+ group_by_mop_condition=group_by_mop_condition,
+ conditions=conditions,
+ order_by=order_by
+ ), filters, as_dict=1)
+
+def concat_mode_of_payments(pos_entries):
+ mode_of_payments = get_mode_of_payments(set([d.pos_invoice for d in pos_entries]))
+ for entry in pos_entries:
+ if mode_of_payments.get(entry.pos_invoice):
+ entry.mode_of_payment = ", ".join(mode_of_payments.get(entry.pos_invoice, []))
+
+def add_subtotal_row(data, group_invoices, group_by_field, group_by_value):
+ grand_total = sum([d.grand_total for d in group_invoices])
+ paid_amount = sum([d.paid_amount for d in group_invoices])
+ data.append({
+ group_by_field: group_by_value,
+ "grand_total": grand_total,
+ "paid_amount": paid_amount,
+ "bold": 1
+ })
+ data.append({})
+
+def validate_filters(filters):
+ if not filters.get("company"):
+ frappe.throw(_("{0} is mandatory").format(_("Company")))
+
+ if not filters.get("from_date") and not filters.get("to_date"):
+ frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
+
+ if filters.from_date > filters.to_date:
+ frappe.throw(_("From Date must be before To Date"))
+
+ if (filters.get("pos_profile") and filters.get("group_by") == _('POS Profile')):
+ frappe.throw(_("Can not filter based on POS Profile, if grouped by POS Profile"))
+
+ if (filters.get("customer") and filters.get("group_by") == _('Customer')):
+ frappe.throw(_("Can not filter based on Customer, if grouped by Customer"))
+
+ if (filters.get("owner") and filters.get("group_by") == _('Cashier')):
+ frappe.throw(_("Can not filter based on Cashier, if grouped by Cashier"))
+
+ if (filters.get("mode_of_payment") and filters.get("group_by") == _('Payment Method')):
+ frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method"))
+
+def get_conditions(filters):
+ conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s".format(
+ company=filters.get("company"),
+ from_date=filters.get("from_date"),
+ to_date=filters.get("to_date"))
+
+ if filters.get("pos_profile"):
+ conditions += " AND pos_profile = %(pos_profile)s".format(pos_profile=filters.get("pos_profile"))
+
+ if filters.get("owner"):
+ conditions += " AND owner = %(owner)s".format(owner=filters.get("owner"))
+
+ if filters.get("customer"):
+ conditions += " AND customer = %(customer)s".format(customer=filters.get("customer"))
+
+ if filters.get("is_return"):
+ conditions += " AND is_return = %(is_return)s".format(is_return=filters.get("is_return"))
+
+ if filters.get("mode_of_payment"):
+ conditions += """
+ AND EXISTS(
+ SELECT name FROM `tabSales Invoice Payment` sip
+ WHERE parent=p.name AND ifnull(sip.mode_of_payment, '') = %(mode_of_payment)s
+ )"""
+
+ return conditions
+
+def get_group_by_field(group_by):
+ group_by_field = ""
+
+ if group_by == "POS Profile":
+ group_by_field = "pos_profile"
+ elif group_by == "Cashier":
+ group_by_field = "owner"
+ elif group_by == "Customer":
+ group_by_field = "customer"
+ elif group_by == "Payment Method":
+ group_by_field = "mode_of_payment"
+
+ return group_by_field
+
+def get_columns(filters):
+ columns = [
+ {
+ "label": _("Posting Date"),
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "width": 90
+ },
+ {
+ "label": _("POS Invoice"),
+ "fieldname": "pos_invoice",
+ "fieldtype": "Link",
+ "options": "POS Invoice",
+ "width": 120
+ },
+ {
+ "label": _("Customer"),
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "options": "Customer",
+ "width": 120
+ },
+ {
+ "label": _("POS Profile"),
+ "fieldname": "pos_profile",
+ "fieldtype": "Link",
+ "options": "POS Profile",
+ "width": 160
+ },
+ {
+ "label": _("Cashier"),
+ "fieldname": "owner",
+ "fieldtype": "Link",
+ "options": "User",
+ "width": 140
+ },
+ {
+ "label": _("Grand Total"),
+ "fieldname": "grand_total",
+ "fieldtype": "Currency",
+ "options": "company:currency",
+ "width": 120
+ },
+ {
+ "label": _("Paid Amount"),
+ "fieldname": "paid_amount",
+ "fieldtype": "Currency",
+ "options": "company:currency",
+ "width": 120
+ },
+ {
+ "label": _("Payment Method"),
+ "fieldname": "mode_of_payment",
+ "fieldtype": "Data",
+ "width": 150
+ },
+ {
+ "label": _("Is Return"),
+ "fieldname": "is_return",
+ "fieldtype": "Data",
+ "width": 80
+ },
+ ]
+
+ return columns
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index efdbdb1fbf..30abc66a02 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -131,7 +131,7 @@ class Asset(AccountsController):
def validate_gross_and_purchase_amount(self):
if self.is_existing_asset: return
-
+
if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount:
frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\
Please do not book expense of multiple assets against one single Asset.")
@@ -466,50 +466,63 @@ class Asset(AccountsController):
def validate_make_gl_entry(self):
purchase_document = self.get_purchase_document()
- asset_bought_with_invoice = purchase_document == self.purchase_invoice
- fixed_asset_account, cwip_account = self.get_asset_accounts()
- cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
- # check if expense already has been booked in case of cwip was enabled after purchasing asset
- expense_booked = False
- cwip_booked = False
-
- if asset_bought_with_invoice:
- expense_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
- (purchase_document, fixed_asset_account), as_dict=1)
- else:
- cwip_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
- (purchase_document, cwip_account), as_dict=1)
-
- if cwip_enabled and (expense_booked or not cwip_booked):
- # if expense has already booked from invoice or cwip is booked from receipt
+ if not purchase_document:
return False
- elif not cwip_enabled and (not expense_booked or cwip_booked):
- # if cwip is disabled but expense hasn't been booked yet
- return True
- elif cwip_enabled:
- # default condition
- return True
+
+ asset_bought_with_invoice = (purchase_document == self.purchase_invoice)
+ fixed_asset_account = self.get_fixed_asset_account()
+
+ cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
+ cwip_account = self.get_cwip_account(cwip_enabled=cwip_enabled)
+
+ query = """SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s"""
+ if asset_bought_with_invoice:
+ # with invoice purchase either expense or cwip has been booked
+ expense_booked = frappe.db.sql(query, (purchase_document, fixed_asset_account), as_dict=1)
+ if expense_booked:
+ # if expense is already booked from invoice then do not make gl entries regardless of cwip enabled/disabled
+ return False
+
+ cwip_booked = frappe.db.sql(query, (purchase_document, cwip_account), as_dict=1)
+ if cwip_booked:
+ # if cwip is booked from invoice then make gl entries regardless of cwip enabled/disabled
+ return True
+ else:
+ # with receipt purchase either cwip has been booked or no entries have been made
+ if not cwip_account:
+ # if cwip account isn't available do not make gl entries
+ return False
+
+ cwip_booked = frappe.db.sql(query, (purchase_document, cwip_account), as_dict=1)
+ # if cwip is not booked from receipt then do not make gl entries
+ # if cwip is booked from receipt then make gl entries
+ return cwip_booked
def get_purchase_document(self):
asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')
purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt
return purchase_document
+
+ def get_fixed_asset_account(self):
+ return get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company)
+
+ def get_cwip_account(self, cwip_enabled=False):
+ cwip_account = None
+ try:
+ cwip_account = get_asset_account("capital_work_in_progress_account", self.name, self.asset_category, self.company)
+ except:
+ # if no cwip account found in category or company and "cwip is enabled" then raise else silently pass
+ if cwip_enabled:
+ raise
- def get_asset_accounts(self):
- fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
- asset_category = self.asset_category, company = self.company)
-
- cwip_account = get_asset_account("capital_work_in_progress_account",
- self.name, self.asset_category, self.company)
-
- return fixed_asset_account, cwip_account
+ return cwip_account
def make_gl_entries(self):
gl_entries = []
purchase_document = self.get_purchase_document()
- fixed_asset_account, cwip_account = self.get_asset_accounts()
+ fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account()
if (purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
@@ -561,14 +574,18 @@ class Asset(AccountsController):
return 100 * (1 - flt(depreciation_rate, float_precision))
def update_maintenance_status():
- assets = frappe.get_all('Asset', filters = {'docstatus': 1, 'maintenance_required': 1})
+ assets = frappe.get_all(
+ "Asset", filters={"docstatus": 1, "maintenance_required": 1}
+ )
for asset in assets:
asset = frappe.get_doc("Asset", asset.name)
- if frappe.db.exists('Asset Maintenance Task', {'parent': asset.name, 'next_due_date': today()}):
- asset.set_status('In Maintenance')
- if frappe.db.exists('Asset Repair', {'asset_name': asset.name, 'repair_status': 'Pending'}):
- asset.set_status('Out of Order')
+ if frappe.db.exists("Asset Repair", {"asset_name": asset.name, "repair_status": "Pending"}):
+ asset.set_status("Out of Order")
+ elif frappe.db.exists("Asset Maintenance Task", {"parent": asset.name, "next_due_date": today()}):
+ asset.set_status("In Maintenance")
+ else:
+ asset.set_status()
def make_post_gl_entry():
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 52039c183b..a0d76031fc 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -9,6 +9,7 @@ from frappe.utils import cstr, nowdate, getdate, flt, get_last_day, add_days, ad
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
from erpnext.assets.doctype.asset.asset import make_sales_invoice
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_invoice
class TestAsset(unittest.TestCase):
@@ -558,81 +559,6 @@ class TestAsset(unittest.TestCase):
self.assertEqual(gle, expected_gle)
- def test_gle_with_cwip_toggling(self):
- # TEST: purchase an asset with cwip enabled and then disable cwip and try submitting the asset
- frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
-
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=5000, do_not_submit=True, location="Test Location")
- pr.set('taxes', [{
- 'category': 'Total',
- 'add_deduct_tax': 'Add',
- 'charge_type': 'On Net Total',
- 'account_head': '_Test Account Service Tax - _TC',
- 'description': '_Test Account Service Tax',
- 'cost_center': 'Main - _TC',
- 'rate': 5.0
- }, {
- 'category': 'Valuation and Total',
- 'add_deduct_tax': 'Add',
- 'charge_type': 'On Net Total',
- 'account_head': '_Test Account Shipping Charges - _TC',
- 'description': '_Test Account Shipping Charges',
- 'cost_center': 'Main - _TC',
- 'rate': 5.0
- }])
- pr.submit()
- expected_gle = (
- ("Asset Received But Not Billed - _TC", 0.0, 5250.0),
- ("CWIP Account - _TC", 5250.0, 0.0)
- )
- pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where voucher_type='Purchase Receipt' and voucher_no = %s
- order by account""", pr.name)
- self.assertEqual(pr_gle, expected_gle)
-
- pi = make_invoice(pr.name)
- pi.submit()
- expected_gle = (
- ("_Test Account Service Tax - _TC", 250.0, 0.0),
- ("_Test Account Shipping Charges - _TC", 250.0, 0.0),
- ("Asset Received But Not Billed - _TC", 5250.0, 0.0),
- ("Creditors - _TC", 0.0, 5500.0),
- ("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
- )
- pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where voucher_type='Purchase Invoice' and voucher_no = %s
- order by account""", pi.name)
- self.assertEqual(pi_gle, expected_gle)
-
- asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
- asset_doc = frappe.get_doc('Asset', asset)
- month_end_date = get_last_day(nowdate())
- asset_doc.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
- self.assertEqual(asset_doc.gross_purchase_amount, 5250.0)
- asset_doc.append("finance_books", {
- "expected_value_after_useful_life": 200,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": month_end_date
- })
-
- # disable cwip and try submitting
- frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
- asset_doc.submit()
- # asset should have gl entries even if cwip is disabled
- expected_gle = (
- ("_Test Fixed Asset - _TC", 5250.0, 0.0),
- ("CWIP Account - _TC", 0.0, 5250.0)
- )
- gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where voucher_type='Asset' and voucher_no = %s
- order by account""", asset_doc.name)
- self.assertEqual(gle, expected_gle)
-
- frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
-
def test_expense_head(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=2, rate=200000.0, location="Test Location")
@@ -640,6 +566,74 @@ class TestAsset(unittest.TestCase):
doc = make_invoice(pr.name)
self.assertEquals('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
+
+ def test_asset_cwip_toggling_cases(self):
+ cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting")
+ name = frappe.db.get_value("Asset Category Account", filters={"parent": "Computers"}, fieldname=["name"])
+ cwip_acc = "CWIP Account - _TC"
+
+ frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
+ frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", "")
+ frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", "")
+
+ # case 0 -- PI with cwip disable, Asset with cwip disabled, No cwip account set
+ pi = make_purchase_invoice(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1)
+ asset = frappe.db.get_value('Asset', {'purchase_invoice': pi.name, 'docstatus': 0}, 'name')
+ asset_doc = frappe.get_doc('Asset', asset)
+ asset_doc.available_for_use_date = nowdate()
+ asset_doc.calculate_depreciation = 0
+ asset_doc.submit()
+ gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+ self.assertFalse(gle)
+
+ # case 1 -- PR with cwip disabled, Asset with cwip enabled
+ pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location")
+ frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
+ frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
+ asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
+ asset_doc = frappe.get_doc('Asset', asset)
+ asset_doc.available_for_use_date = nowdate()
+ asset_doc.calculate_depreciation = 0
+ asset_doc.submit()
+ gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+ self.assertFalse(gle)
+
+ # case 2 -- PR with cwip enabled, Asset with cwip disabled
+ pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location")
+ frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
+ asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
+ asset_doc = frappe.get_doc('Asset', asset)
+ asset_doc.available_for_use_date = nowdate()
+ asset_doc.calculate_depreciation = 0
+ asset_doc.submit()
+ gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+ self.assertTrue(gle)
+
+ # case 3 -- PI with cwip disabled, Asset with cwip enabled
+ pi = make_purchase_invoice(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1)
+ frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
+ asset = frappe.db.get_value('Asset', {'purchase_invoice': pi.name, 'docstatus': 0}, 'name')
+ asset_doc = frappe.get_doc('Asset', asset)
+ asset_doc.available_for_use_date = nowdate()
+ asset_doc.calculate_depreciation = 0
+ asset_doc.submit()
+ gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+ self.assertFalse(gle)
+
+ # case 4 -- PI with cwip enabled, Asset with cwip disabled
+ pi = make_purchase_invoice(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1)
+ frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
+ asset = frappe.db.get_value('Asset', {'purchase_invoice': pi.name, 'docstatus': 0}, 'name')
+ asset_doc = frappe.get_doc('Asset', asset)
+ asset_doc.available_for_use_date = nowdate()
+ asset_doc.calculate_depreciation = 0
+ asset_doc.submit()
+ gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+ self.assertTrue(gle)
+
+ frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", cwip)
+ frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
+ frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
def create_asset_data():
if not frappe.db.exists("Asset Category", "Computers"):
diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py
index 9a33fc14ac..46620d56e9 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.py
+++ b/erpnext/assets/doctype/asset_category/asset_category.py
@@ -5,7 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import cint
+from frappe.utils import cint, get_link_to_form
from frappe.model.document import Document
class AssetCategory(Document):
@@ -13,6 +13,7 @@ class AssetCategory(Document):
self.validate_finance_books()
self.validate_account_types()
self.validate_account_currency()
+ self.valide_cwip_account()
def validate_finance_books(self):
for d in self.finance_books:
@@ -58,6 +59,21 @@ class AssetCategory(Document):
frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.")
.format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)),
title=_("Invalid Account"))
+
+ def valide_cwip_account(self):
+ if self.enable_cwip_accounting:
+ missing_cwip_accounts_for_company = []
+ for d in self.accounts:
+ if (not d.capital_work_in_progress_account and
+ not frappe.db.get_value("Company", d.company_name, "capital_work_in_progress_account")):
+ missing_cwip_accounts_for_company.append(get_link_to_form("Company", d.company_name))
+
+ if missing_cwip_accounts_for_company:
+ msg = _("""To enable Capital Work in Progress Accounting, """)
+ msg += _("""you must select Capital Work in Progress Account in accounts table""")
+ msg += "
"
+ msg += _("You can also set default CWIP account in Company {}").format(", ".join(missing_cwip_accounts_for_company))
+ frappe.throw(msg, title=_("Missing Account"))
@frappe.whitelist()
diff --git a/erpnext/assets/doctype/asset_category/test_asset_category.py b/erpnext/assets/doctype/asset_category/test_asset_category.py
index b32f9b5020..39b79d6c50 100644
--- a/erpnext/assets/doctype/asset_category/test_asset_category.py
+++ b/erpnext/assets/doctype/asset_category/test_asset_category.py
@@ -26,4 +26,22 @@ class TestAssetCategory(unittest.TestCase):
asset_category.insert()
except frappe.DuplicateEntryError:
pass
-
\ No newline at end of file
+
+ def test_cwip_accounting(self):
+ company_cwip_acc = frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account")
+ frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "")
+
+ asset_category = frappe.new_doc("Asset Category")
+ asset_category.asset_category_name = "Computers"
+ asset_category.enable_cwip_accounting = 1
+
+ asset_category.total_number_of_depreciations = 3
+ asset_category.frequency_of_depreciation = 3
+ asset_category.append("accounts", {
+ "company_name": "_Test Company",
+ "fixed_asset_account": "_Test Fixed Asset - _TC",
+ "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
+ "depreciation_expense_account": "_Test Depreciations - _TC"
+ })
+
+ self.assertRaises(frappe.ValidationError, asset_category.insert)
\ No newline at end of file
diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json
index 565d39c3c8..2e870fea82 100644
--- a/erpnext/buying/desk_page/buying/buying.json
+++ b/erpnext/buying/desk_page/buying/buying.json
@@ -33,7 +33,7 @@
{
"hidden": 0,
"label": "Other Reports",
- "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]"
+ "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Quotation Comparison\",\n \"name\": \"Supplier Quotation Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
@@ -60,7 +60,7 @@
"idx": 0,
"is_standard": 1,
"label": "Buying",
- "modified": "2020-06-29 19:30:24.983050",
+ "modified": "2020-09-30 14:40:55.638458",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying",
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 158799ce63..7c8ae6cfb8 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -651,12 +651,12 @@ class TestPurchaseOrder(unittest.TestCase):
make_subcontracted_item(item_code)
po = create_purchase_order(item_code=item_code, qty=1,
- is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
+ is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=1)
name = frappe.db.get_value('BOM', {'item': item_code}, 'name')
bom = frappe.get_doc('BOM', name)
- exploded_items = sorted([d.item_code for d in bom.exploded_items])
+ exploded_items = sorted([d.item_code for d in bom.exploded_items if not d.get('sourced_by_supplier')])
supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
self.assertEquals(exploded_items, supplied_items)
@@ -664,7 +664,7 @@ class TestPurchaseOrder(unittest.TestCase):
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=0)
supplied_items1 = sorted([d.rm_item_code for d in po1.supplied_items])
- bom_items = sorted([d.item_code for d in bom.items])
+ bom_items = sorted([d.item_code for d in bom.items if not d.get('sourced_by_supplier')])
self.assertEquals(supplied_items1, bom_items)
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index 4a937f7f0d..cf5d7cd4c5 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -179,7 +179,7 @@ frappe.ui.form.on("Request for Quotation",{
dialog.hide();
return frappe.call({
type: "GET",
- method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.make_supplier_quotation",
+ method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.make_supplier_quotation_from_rfq",
args: {
"source_name": doc.name,
"for_supplier": args.supplier
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index b54a585b97..361ccdf7f3 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -214,14 +214,14 @@ def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters):
and `tabDynamic Link`.link_name like %(txt)s) and `tabContact`.name = `tabDynamic Link`.parent
limit %(start)s, %(page_len)s""", {"start": start, "page_len":page_len, "txt": "%%%s%%" % txt, "name": filters.get('supplier')})
-# This method is used to make supplier quotation from material request form.
@frappe.whitelist()
-def make_supplier_quotation(source_name, for_supplier, target_doc=None):
+def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=None):
def postprocess(source, target_doc):
- target_doc.supplier = for_supplier
- args = get_party_details(for_supplier, party_type="Supplier", ignore_permissions=True)
- target_doc.currency = args.currency or get_party_account_currency('Supplier', for_supplier, source.company)
- target_doc.buying_price_list = args.buying_price_list or frappe.db.get_value('Buying Settings', None, 'buying_price_list')
+ if for_supplier:
+ target_doc.supplier = for_supplier
+ args = get_party_details(for_supplier, party_type="Supplier", ignore_permissions=True)
+ target_doc.currency = args.currency or get_party_account_currency('Supplier', for_supplier, source.company)
+ target_doc.buying_price_list = args.buying_price_list or frappe.db.get_value('Buying Settings', None, 'buying_price_list')
set_missing_values(source, target_doc)
doclist = get_mapped_doc("Request for Quotation", source_name, {
@@ -354,3 +354,32 @@ def get_supplier_tag():
frappe.cache().hset("Supplier", "Tags", tags)
return frappe.cache().hget("Supplier", "Tags")
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_rfq_containing_supplier(doctype, txt, searchfield, start, page_len, filters):
+ conditions = ""
+ if txt:
+ conditions += "and rfq.name like '%%"+txt+"%%' "
+
+ if filters.get("transaction_date"):
+ conditions += "and rfq.transaction_date = '{0}'".format(filters.get("transaction_date"))
+
+ rfq_data = frappe.db.sql("""
+ select
+ distinct rfq.name, rfq.transaction_date,
+ rfq.company
+ from
+ `tabRequest for Quotation` rfq, `tabRequest for Quotation Supplier` rfq_supplier
+ where
+ rfq.name = rfq_supplier.parent
+ and rfq_supplier.supplier = '{0}'
+ and rfq.docstatus = 1
+ and rfq.company = '{1}'
+ {2}
+ order by rfq.transaction_date ASC
+ limit %(page_len)s offset %(start)s """ \
+ .format(filters.get("supplier"), filters.get("company"), conditions),
+ {"page_len": page_len, "start": start}, as_dict=1)
+
+ return rfq_data
\ No newline at end of file
diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
index 019cefc0bd..ea38129a70 100644
--- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
@@ -9,7 +9,7 @@ import frappe
from frappe.utils import nowdate
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.templates.pages.rfq import check_supplier_has_docname_access
-from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
+from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation_from_rfq
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation
from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
@@ -22,7 +22,7 @@ class TestRequestforQuotation(unittest.TestCase):
self.assertEqual(rfq.get('suppliers')[1].quote_status, 'Pending')
# Submit the first supplier quotation
- sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier)
+ sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[0].supplier)
sq.submit()
# No Quote first supplier quotation
@@ -37,10 +37,10 @@ class TestRequestforQuotation(unittest.TestCase):
def test_make_supplier_quotation(self):
rfq = make_request_for_quotation()
- sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier)
+ sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[0].supplier)
sq.submit()
- sq1 = make_supplier_quotation(rfq.name, rfq.get('suppliers')[1].supplier)
+ sq1 = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[1].supplier)
sq1.submit()
self.assertEqual(sq.supplier, rfq.get('suppliers')[0].supplier)
@@ -62,7 +62,7 @@ class TestRequestforQuotation(unittest.TestCase):
rfq = make_request_for_quotation(supplier_data=supplier_wt_appos)
- sq = make_supplier_quotation(rfq.name, supplier_wt_appos[0].get("supplier"))
+ sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=supplier_wt_appos[0].get("supplier"))
sq.submit()
frappe.form_dict = frappe.local("form_dict")
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
index 1b8b40459f..3376e82956 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
@@ -8,8 +8,7 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
setup: function() {
this.frm.custom_make_buttons = {
'Purchase Order': 'Purchase Order',
- 'Quotation': 'Quotation',
- 'Subscription': 'Subscription'
+ 'Quotation': 'Quotation'
}
this._super();
@@ -28,12 +27,6 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
cur_frm.add_custom_button(__("Quotation"), this.make_quotation,
__('Create'));
-
- if(!this.frm.doc.auto_repeat) {
- cur_frm.add_custom_button(__('Subscription'), function() {
- erpnext.utils.make_subscription(me.frm.doc.doctype, me.frm.doc.name)
- }, __('Create'))
- }
}
else if (this.frm.doc.docstatus===0) {
@@ -54,6 +47,27 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
}
})
}, __("Get items from"));
+
+ this.frm.add_custom_button(__("Request for Quotation"),
+ function() {
+ if (!me.frm.doc.supplier) {
+ frappe.throw({message:__("Please select a Supplier"), title:__("Mandatory")})
+ }
+ erpnext.utils.map_current_doc({
+ method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.make_supplier_quotation_from_rfq",
+ source_doctype: "Request for Quotation",
+ target: me.frm,
+ setters: {
+ company: me.frm.doc.company,
+ transaction_date: null
+ },
+ get_query_filters: {
+ supplier: me.frm.doc.supplier
+ },
+ get_query_method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_rfq_containing_supplier"
+
+ })
+ }, __("Get items from"));
}
},
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 660dcff34b..9a092ca5c3 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -159,6 +159,7 @@
"default": "Today",
"fieldname": "transaction_date",
"fieldtype": "Date",
+ "in_list_view": 1,
"label": "Date",
"oldfieldname": "transaction_date",
"oldfieldtype": "Date",
@@ -798,6 +799,7 @@
{
"fieldname": "valid_till",
"fieldtype": "Date",
+ "in_list_view": 1,
"label": "Valid Till"
}
],
@@ -805,7 +807,7 @@
"idx": 29,
"is_submittable": 1,
"links": [],
- "modified": "2020-07-18 05:10:45.556792",
+ "modified": "2020-10-01 20:56:17.932007",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",
diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
index b50e834ec7..7d624357f3 100644
--- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
+++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
@@ -12,6 +12,8 @@
"item_name",
"column_break_3",
"lead_time_days",
+ "expected_delivery_date",
+ "is_free_item",
"section_break_5",
"description",
"item_group",
@@ -19,20 +21,18 @@
"col_break1",
"image",
"image_view",
- "manufacture_details",
- "manufacturer",
- "column_break_15",
- "manufacturer_part_no",
"quantity_and_rate",
"qty",
"stock_uom",
- "price_list_rate",
- "discount_percentage",
- "discount_amount",
"col_break2",
"uom",
"conversion_factor",
"stock_qty",
+ "sec_break_price_list",
+ "price_list_rate",
+ "discount_percentage",
+ "discount_amount",
+ "col_break_price_list",
"base_price_list_rate",
"sec_break1",
"rate",
@@ -42,7 +42,6 @@
"base_rate",
"base_amount",
"pricing_rules",
- "is_free_item",
"section_break_24",
"net_rate",
"net_amount",
@@ -56,7 +55,6 @@
"weight_uom",
"warehouse_and_reference",
"warehouse",
- "project",
"prevdoc_doctype",
"material_request",
"sales_order",
@@ -65,13 +63,19 @@
"material_request_item",
"request_for_quotation_item",
"item_tax_rate",
+ "manufacture_details",
+ "manufacturer",
+ "column_break_15",
+ "manufacturer_part_no",
+ "ad_sec_break",
+ "project",
"section_break_44",
"page_break"
],
"fields": [
{
"bold": 1,
- "columns": 4,
+ "columns": 2,
"fieldname": "item_code",
"fieldtype": "Link",
"in_list_view": 1,
@@ -107,7 +111,7 @@
{
"fieldname": "lead_time_days",
"fieldtype": "Int",
- "label": "Lead Time in days"
+ "label": "Supplier Lead Time (days)"
},
{
"collapsible": 1,
@@ -162,7 +166,6 @@
{
"fieldname": "stock_uom",
"fieldtype": "Link",
- "in_list_view": 1,
"label": "Stock UOM",
"options": "UOM",
"print_hide": 1,
@@ -196,6 +199,7 @@
{
"fieldname": "uom",
"fieldtype": "Link",
+ "in_list_view": 1,
"label": "UOM",
"options": "UOM",
"print_hide": 1,
@@ -289,14 +293,6 @@
"print_hide": 1,
"read_only": 1
},
- {
- "default": "0",
- "fieldname": "is_free_item",
- "fieldtype": "Check",
- "label": "Is Free Item",
- "print_hide": 1,
- "read_only": 1
- },
{
"fieldname": "section_break_24",
"fieldtype": "Section Break"
@@ -528,12 +524,43 @@
{
"fieldname": "column_break_15",
"fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "sec_break_price_list",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "col_break_price_list",
+ "fieldtype": "Column Break"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "ad_sec_break",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "default": "0",
+ "depends_on": "is_free_item",
+ "fieldname": "is_free_item",
+ "fieldtype": "Check",
+ "label": "Is Free Item",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 1,
+ "fieldname": "expected_delivery_date",
+ "fieldtype": "Date",
+ "label": "Expected Delivery Date"
}
],
"idx": 1,
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-07 18:35:51.175947",
+ "modified": "2020-10-01 16:34:39.703033",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation Item",
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.json b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.json
deleted file mode 100644
index 23b3ace49c..0000000000
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2016-07-21 08:31:05.890362",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 2,
- "is_standard": "Yes",
- "modified": "2017-02-24 20:04:58.784351",
- "modified_by": "Administrator",
- "module": "Buying",
- "name": "Quoted Item Comparison",
- "owner": "Administrator",
- "ref_doctype": "Supplier Quotation",
- "report_name": "Quoted Item Comparison",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "Manufacturing Manager"
- },
- {
- "role": "Purchase Manager"
- },
- {
- "role": "Purchase User"
- },
- {
- "role": "Stock User"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/buying/report/supplier_quotation_comparison/__init__.py b/erpnext/buying/report/supplier_quotation_comparison/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.html b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html
similarity index 100%
rename from erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.html
rename to erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
similarity index 91%
rename from erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
rename to erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
index 518d665e7e..80e521a8bf 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
@@ -1,7 +1,7 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
-frappe.query_reports["Quoted Item Comparison"] = {
+frappe.query_reports["Supplier Quotation Comparison"] = {
filters: [
{
fieldtype: "Link",
@@ -78,6 +78,13 @@ frappe.query_reports["Quoted Item Comparison"] = {
return { filters: { "docstatus": ["<", 2] } }
}
},
+ {
+ "fieldname":"group_by",
+ "label": __("Group by"),
+ "fieldtype": "Select",
+ "options": [__("Group by Supplier"), __("Group by Item")],
+ "default": __("Group by Supplier")
+ },
{
fieldtype: "Check",
label: __("Include Expired"),
@@ -98,6 +105,9 @@ frappe.query_reports["Quoted Item Comparison"] = {
}
}
+ if(column.fieldname === "price_per_unit" && data.price_per_unit && data.min && data.min === 1){
+ value = `
Notes:
\n\nbase
for using base salary of the EmployeeBS = Basic Salary
Employment Type = employment_type
Branch = branch
Payment Days = payment_days
Leave without pay = leave_without_pay
base
\nCondition: base < 10000
\nFormula: base * .2
BS
\nCondition: BS > 2000
\nFormula: BS * .1
employment_type
\nCondition: employment_type==\"Intern\"
\nAmount: 1000
Notes:
\n\nbase
for using base salary of the EmployeeBS = Basic Salary
Employment Type = employment_type
Branch = branch
Payment Days = payment_days
Leave without pay = leave_without_pay
base
\nCondition: base < 10000
\nFormula: base * .2
BS
\nCondition: BS > 2000
\nFormula: BS * .1
employment_type
\nCondition: employment_type==\"Intern\"
\nAmount: 1000