Merge branch 'develop' of https://github.com/frappe/erpnext into pr-dn-return
This commit is contained in:
commit
28dd90f3dc
@ -7,7 +7,7 @@ frappe.ui.form.on('Accounting Dimension', {
|
|||||||
frm.set_query('document_type', () => {
|
frm.set_query('document_type', () => {
|
||||||
let invalid_doctypes = frappe.model.core_doctypes_list;
|
let invalid_doctypes = frappe.model.core_doctypes_list;
|
||||||
invalid_doctypes.push('Accounting Dimension', 'Project',
|
invalid_doctypes.push('Accounting Dimension', 'Project',
|
||||||
'Cost Center', 'Accounting Dimension Detail');
|
'Cost Center', 'Accounting Dimension Detail', 'Company');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
|
@ -19,7 +19,7 @@ class AccountingDimension(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project',
|
if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project',
|
||||||
'Cost Center', 'Accounting Dimension Detail') :
|
'Cost Center', 'Accounting Dimension Detail', 'Company') :
|
||||||
|
|
||||||
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
||||||
frappe.throw(msg)
|
frappe.throw(msg)
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"column_break_9",
|
"column_break_9",
|
||||||
"update_stock",
|
"update_stock",
|
||||||
"ignore_pricing_rule",
|
"ignore_pricing_rule",
|
||||||
|
"hide_unavailable_items",
|
||||||
"warehouse",
|
"warehouse",
|
||||||
"campaign",
|
"campaign",
|
||||||
"company_address",
|
"company_address",
|
||||||
@ -307,13 +308,19 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Update Stock",
|
"label": "Update Stock",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "hide_unavailable_items",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Hide Unavailable Items"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-20 13:16:50.665081",
|
"modified": "2020-10-29 13:18:38.795925",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Profile",
|
"name": "POS Profile",
|
||||||
|
@ -504,10 +504,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:in_list(['Discount Percentage', 'Discount Amount'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules",
|
"depends_on": "eval:in_list(['Discount Percentage'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules",
|
||||||
"fieldname": "apply_discount_on_rate",
|
"fieldname": "apply_discount_on_rate",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Apply Discount on Rate"
|
"label": "Apply Discount on Discounted Rate"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -563,7 +563,7 @@
|
|||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-08-26 12:24:44.740734",
|
"modified": "2020-10-28 16:53:14.416172",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
@ -60,6 +60,15 @@ class PricingRule(Document):
|
|||||||
if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
|
if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
|
||||||
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
|
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
|
||||||
|
|
||||||
|
if self.apply_discount_on_rate:
|
||||||
|
if not self.priority:
|
||||||
|
throw(_("As the field {0} is enabled, the field {1} is mandatory.")
|
||||||
|
.format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")))
|
||||||
|
|
||||||
|
if self.priority and cint(self.priority) == 1:
|
||||||
|
throw(_("As the field {0} is enabled, the value of the field {1} should be more than 1.")
|
||||||
|
.format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")))
|
||||||
|
|
||||||
def validate_applicable_for_selling_or_buying(self):
|
def validate_applicable_for_selling_or_buying(self):
|
||||||
if not self.selling and not self.buying:
|
if not self.selling and not self.buying:
|
||||||
throw(_("Atleast one of the Selling or Buying must be selected"))
|
throw(_("Atleast one of the Selling or Buying must be selected"))
|
||||||
@ -226,12 +235,11 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
|
|
||||||
item_details = frappe._dict({
|
item_details = frappe._dict({
|
||||||
"doctype": args.doctype,
|
"doctype": args.doctype,
|
||||||
|
"has_margin": False,
|
||||||
"name": args.name,
|
"name": args.name,
|
||||||
"parent": args.parent,
|
"parent": args.parent,
|
||||||
"parenttype": args.parenttype,
|
"parenttype": args.parenttype,
|
||||||
"child_docname": args.get('child_docname'),
|
"child_docname": args.get('child_docname')
|
||||||
"discount_percentage_on_rate": [],
|
|
||||||
"discount_amount_on_rate": []
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if args.ignore_pricing_rule or not args.item_code:
|
if args.ignore_pricing_rule or not args.item_code:
|
||||||
@ -279,6 +287,10 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
else:
|
else:
|
||||||
get_product_discount_rule(pricing_rule, item_details, args, doc)
|
get_product_discount_rule(pricing_rule, item_details, args, doc)
|
||||||
|
|
||||||
|
if not item_details.get("has_margin"):
|
||||||
|
item_details.margin_type = None
|
||||||
|
item_details.margin_rate_or_amount = 0.0
|
||||||
|
|
||||||
item_details.has_pricing_rule = 1
|
item_details.has_pricing_rule = 1
|
||||||
|
|
||||||
item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules])
|
item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules])
|
||||||
@ -330,13 +342,11 @@ def get_pricing_rule_details(args, pricing_rule):
|
|||||||
def apply_price_discount_rule(pricing_rule, item_details, args):
|
def apply_price_discount_rule(pricing_rule, item_details, args):
|
||||||
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
||||||
|
|
||||||
if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
|
if ((pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == args.currency)
|
||||||
or (pricing_rule.margin_type == 'Percentage')):
|
or (pricing_rule.margin_type == 'Percentage')):
|
||||||
item_details.margin_type = pricing_rule.margin_type
|
item_details.margin_type = pricing_rule.margin_type
|
||||||
item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
|
item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
|
||||||
else:
|
item_details.has_margin = True
|
||||||
item_details.margin_type = None
|
|
||||||
item_details.margin_rate_or_amount = 0.0
|
|
||||||
|
|
||||||
if pricing_rule.rate_or_discount == 'Rate':
|
if pricing_rule.rate_or_discount == 'Rate':
|
||||||
pricing_rule_rate = 0.0
|
pricing_rule_rate = 0.0
|
||||||
@ -351,9 +361,9 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
|
|||||||
if pricing_rule.rate_or_discount != apply_on: continue
|
if pricing_rule.rate_or_discount != apply_on: continue
|
||||||
|
|
||||||
field = frappe.scrub(apply_on)
|
field = frappe.scrub(apply_on)
|
||||||
if pricing_rule.apply_discount_on_rate:
|
if pricing_rule.apply_discount_on_rate and item_details.get("discount_percentage"):
|
||||||
discount_field = "{0}_on_rate".format(field)
|
# Apply discount on discounted rate
|
||||||
item_details[discount_field].append(pricing_rule.get(field, 0))
|
item_details[field] += ((100 - item_details[field]) * (pricing_rule.get(field, 0) / 100))
|
||||||
else:
|
else:
|
||||||
if field not in item_details:
|
if field not in item_details:
|
||||||
item_details.setdefault(field, 0)
|
item_details.setdefault(field, 0)
|
||||||
@ -361,14 +371,6 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
|
|||||||
item_details[field] += (pricing_rule.get(field, 0)
|
item_details[field] += (pricing_rule.get(field, 0)
|
||||||
if pricing_rule else args.get(field, 0))
|
if pricing_rule else args.get(field, 0))
|
||||||
|
|
||||||
def set_discount_amount(rate, item_details):
|
|
||||||
for field in ['discount_percentage_on_rate', 'discount_amount_on_rate']:
|
|
||||||
for d in item_details.get(field):
|
|
||||||
dis_amount = (rate * d / 100
|
|
||||||
if field == 'discount_percentage_on_rate' else d)
|
|
||||||
rate -= dis_amount
|
|
||||||
item_details.rate = rate
|
|
||||||
|
|
||||||
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import (get_applied_pricing_rules,
|
from erpnext.accounts.doctype.pricing_rule.utils import (get_applied_pricing_rules,
|
||||||
get_pricing_rule_items)
|
get_pricing_rule_items)
|
||||||
|
@ -385,7 +385,7 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
so.load_from_db()
|
so.load_from_db()
|
||||||
self.assertEqual(so.items[1].is_free_item, 1)
|
self.assertEqual(so.items[1].is_free_item, 1)
|
||||||
self.assertEqual(so.items[1].item_code, "_Test Item 2")
|
self.assertEqual(so.items[1].item_code, "_Test Item 2")
|
||||||
|
|
||||||
def test_cumulative_pricing_rule(self):
|
def test_cumulative_pricing_rule(self):
|
||||||
frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule')
|
frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule')
|
||||||
test_record = {
|
test_record = {
|
||||||
@ -429,34 +429,61 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
details = get_item_details(args)
|
details = get_item_details(args)
|
||||||
|
|
||||||
self.assertTrue(details)
|
self.assertTrue(details)
|
||||||
|
|
||||||
def test_pricing_rule_for_condition(self):
|
def test_pricing_rule_for_condition(self):
|
||||||
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
|
||||||
|
|
||||||
make_pricing_rule(selling=1, margin_type="Percentage", \
|
make_pricing_rule(selling=1, margin_type="Percentage", \
|
||||||
condition="customer=='_Test Customer 1' and is_return==0", discount_percentage=10)
|
condition="customer=='_Test Customer 1' and is_return==0", discount_percentage=10)
|
||||||
|
|
||||||
# Incorrect Customer and Correct is_return value
|
# Incorrect Customer and Correct is_return value
|
||||||
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 2", is_return=0)
|
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 2", is_return=0)
|
||||||
si.items[0].price_list_rate = 1000
|
si.items[0].price_list_rate = 1000
|
||||||
si.submit()
|
si.submit()
|
||||||
item = si.items[0]
|
item = si.items[0]
|
||||||
self.assertEquals(item.rate, 100)
|
self.assertEquals(item.rate, 100)
|
||||||
|
|
||||||
# Correct Customer and Incorrect is_return value
|
# Correct Customer and Incorrect is_return value
|
||||||
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=1, qty=-1)
|
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=1, qty=-1)
|
||||||
si.items[0].price_list_rate = 1000
|
si.items[0].price_list_rate = 1000
|
||||||
si.submit()
|
si.submit()
|
||||||
item = si.items[0]
|
item = si.items[0]
|
||||||
self.assertEquals(item.rate, 100)
|
self.assertEquals(item.rate, 100)
|
||||||
|
|
||||||
# Correct Customer and correct is_return value
|
# Correct Customer and correct is_return value
|
||||||
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0)
|
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0)
|
||||||
si.items[0].price_list_rate = 1000
|
si.items[0].price_list_rate = 1000
|
||||||
si.submit()
|
si.submit()
|
||||||
item = si.items[0]
|
item = si.items[0]
|
||||||
self.assertEquals(item.rate, 900)
|
self.assertEquals(item.rate, 900)
|
||||||
|
|
||||||
|
def test_multiple_pricing_rules(self):
|
||||||
|
make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1,
|
||||||
|
title="_Test Pricing Rule 1")
|
||||||
|
make_pricing_rule(discount_percentage=10, selling=1, title="_Test Pricing Rule 2", priority=2,
|
||||||
|
apply_multiple_pricing_rules=1)
|
||||||
|
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
|
||||||
|
self.assertEqual(si.items[0].discount_percentage, 30)
|
||||||
|
si.delete()
|
||||||
|
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
|
||||||
|
|
||||||
|
def test_multiple_pricing_rules_with_apply_discount_on_discounted_rate(self):
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
|
||||||
|
|
||||||
|
make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1,
|
||||||
|
title="_Test Pricing Rule 1")
|
||||||
|
make_pricing_rule(discount_percentage=10, selling=1, priority=2,
|
||||||
|
apply_discount_on_rate=1, title="_Test Pricing Rule 2", apply_multiple_pricing_rules=1)
|
||||||
|
|
||||||
|
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
|
||||||
|
self.assertEqual(si.items[0].discount_percentage, 28)
|
||||||
|
si.delete()
|
||||||
|
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
|
||||||
|
|
||||||
def make_pricing_rule(**args):
|
def make_pricing_rule(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
@ -468,6 +495,7 @@ def make_pricing_rule(**args):
|
|||||||
"applicable_for": args.applicable_for,
|
"applicable_for": args.applicable_for,
|
||||||
"selling": args.selling or 0,
|
"selling": args.selling or 0,
|
||||||
"currency": "USD",
|
"currency": "USD",
|
||||||
|
"apply_discount_on_rate": args.apply_discount_on_rate or 0,
|
||||||
"buying": args.buying or 0,
|
"buying": args.buying or 0,
|
||||||
"min_qty": args.min_qty or 0.0,
|
"min_qty": args.min_qty or 0.0,
|
||||||
"max_qty": args.max_qty or 0.0,
|
"max_qty": args.max_qty or 0.0,
|
||||||
@ -476,9 +504,13 @@ def make_pricing_rule(**args):
|
|||||||
"rate": args.rate or 0.0,
|
"rate": args.rate or 0.0,
|
||||||
"margin_type": args.margin_type,
|
"margin_type": args.margin_type,
|
||||||
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
||||||
"condition": args.condition or ''
|
"condition": args.condition or '',
|
||||||
|
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if args.get("priority"):
|
||||||
|
doc.priority = args.get("priority")
|
||||||
|
|
||||||
apply_on = doc.apply_on.replace(' ', '_').lower()
|
apply_on = doc.apply_on.replace(' ', '_').lower()
|
||||||
child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
|
child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
|
||||||
doc.append(child_table.get(doc.apply_on), {
|
doc.append(child_table.get(doc.apply_on), {
|
||||||
|
@ -14,9 +14,8 @@ import frappe
|
|||||||
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
||||||
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||||
from erpnext.stock.get_item_details import get_conversion_factor
|
from erpnext.stock.get_item_details import get_conversion_factor
|
||||||
from frappe import _, throw
|
from frappe import _, bold
|
||||||
from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today
|
from frappe.utils import cint, flt, get_link_to_form, getdate, today, fmt_money
|
||||||
|
|
||||||
|
|
||||||
class MultiplePricingRuleConflict(frappe.ValidationError): pass
|
class MultiplePricingRuleConflict(frappe.ValidationError): pass
|
||||||
|
|
||||||
@ -42,6 +41,7 @@ def get_pricing_rules(args, doc=None):
|
|||||||
if not pricing_rules: return []
|
if not pricing_rules: return []
|
||||||
|
|
||||||
if apply_multiple_pricing_rules(pricing_rules):
|
if apply_multiple_pricing_rules(pricing_rules):
|
||||||
|
pricing_rules = sorted_by_priority(pricing_rules)
|
||||||
for pricing_rule in pricing_rules:
|
for pricing_rule in pricing_rules:
|
||||||
pricing_rule = filter_pricing_rules(args, pricing_rule, doc)
|
pricing_rule = filter_pricing_rules(args, pricing_rule, doc)
|
||||||
if pricing_rule:
|
if pricing_rule:
|
||||||
@ -53,6 +53,20 @@ def get_pricing_rules(args, doc=None):
|
|||||||
|
|
||||||
return rules
|
return rules
|
||||||
|
|
||||||
|
def sorted_by_priority(pricing_rules):
|
||||||
|
# If more than one pricing rules, then sort by priority
|
||||||
|
pricing_rules_list = []
|
||||||
|
pricing_rule_dict = {}
|
||||||
|
for pricing_rule in pricing_rules:
|
||||||
|
if not pricing_rule.get("priority"): continue
|
||||||
|
|
||||||
|
pricing_rule_dict.setdefault(cint(pricing_rule.get("priority")), []).append(pricing_rule)
|
||||||
|
|
||||||
|
for key in sorted(pricing_rule_dict):
|
||||||
|
pricing_rules_list.append(pricing_rule_dict.get(key))
|
||||||
|
|
||||||
|
return pricing_rules_list or pricing_rules
|
||||||
|
|
||||||
def filter_pricing_rule_based_on_condition(pricing_rules, doc=None):
|
def filter_pricing_rule_based_on_condition(pricing_rules, doc=None):
|
||||||
filtered_pricing_rules = []
|
filtered_pricing_rules = []
|
||||||
if doc:
|
if doc:
|
||||||
@ -284,12 +298,13 @@ def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, tr
|
|||||||
fieldname = field
|
fieldname = field
|
||||||
|
|
||||||
if fieldname:
|
if fieldname:
|
||||||
msg = _("""If you {0} {1} quantities of the item <b>{2}</b>, the scheme <b>{3}</b>
|
msg = (_("If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item.")
|
||||||
will be applied on the item.""").format(type_of_transaction, args.get(fieldname), item_code, args.rule_description)
|
.format(type_of_transaction, args.get(fieldname), bold(item_code), bold(args.rule_description)))
|
||||||
|
|
||||||
if fieldname in ['min_amt', 'max_amt']:
|
if fieldname in ['min_amt', 'max_amt']:
|
||||||
msg = _("""If you {0} {1} worth item <b>{2}</b>, the scheme <b>{3}</b> will be applied on the item.
|
msg = (_("If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.")
|
||||||
""").format(frappe.fmt_money(type_of_transaction, args.get(fieldname)), item_code, args.rule_description)
|
.format(type_of_transaction, fmt_money(args.get(fieldname), currency=args.get("currency")),
|
||||||
|
bold(item_code), bold(args.rule_description)))
|
||||||
|
|
||||||
frappe.msgprint(msg)
|
frappe.msgprint(msg)
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ class TestSubscription(unittest.TestCase):
|
|||||||
subscription.party_type = 'Customer'
|
subscription.party_type = 'Customer'
|
||||||
subscription.party = '_Test Customer'
|
subscription.party = '_Test Customer'
|
||||||
subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
|
subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
|
||||||
subscription.start_date = '2018-01-01'
|
subscription.start_date = add_days(nowdate(), -1000)
|
||||||
subscription.insert()
|
subscription.insert()
|
||||||
subscription.process() # generate first invoice
|
subscription.process() # generate first invoice
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ def get_pos_entries(filters, group_by_field):
|
|||||||
FROM
|
FROM
|
||||||
`tabPOS Invoice` p {from_sales_invoice_payment}
|
`tabPOS Invoice` p {from_sales_invoice_payment}
|
||||||
WHERE
|
WHERE
|
||||||
|
p.docstatus = 1 and
|
||||||
{group_by_mop_condition}
|
{group_by_mop_condition}
|
||||||
{conditions}
|
{conditions}
|
||||||
ORDER BY
|
ORDER BY
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
"filters_json": "{\"status\":\"In Location\",\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"date_based_on\":\"Purchase Date\",\"group_by\":\"--Select a group--\"}",
|
"filters_json": "{\"status\":\"In Location\",\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"date_based_on\":\"Purchase Date\",\"group_by\":\"--Select a group--\"}",
|
||||||
"group_by_type": "Count",
|
"group_by_type": "Count",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_public": 0,
|
"is_public": 1,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"modified": "2020-07-23 13:53:33.211371",
|
"modified": "2020-10-28 23:15:58.432189",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset Value Analytics",
|
"name": "Asset Value Analytics",
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
|
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
|
||||||
"filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"is_existing_asset\":0}",
|
"filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"is_existing_asset\":0}",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_public": 0,
|
"is_public": 1,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"modified": "2020-07-23 13:39:32.429240",
|
"modified": "2020-10-28 23:16:16.939070",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Category-wise Asset Value",
|
"name": "Category-wise Asset Value",
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
|
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
|
||||||
"filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"is_existing_asset\":0}",
|
"filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"is_existing_asset\":0}",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_public": 0,
|
"is_public": 1,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"modified": "2020-07-23 13:42:44.912551",
|
"modified": "2020-10-28 23:16:07.883312",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Location-wise Asset Value",
|
"name": "Location-wise Asset Value",
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Depreciation Posting Date",
|
"label": "Depreciation Posting Date",
|
||||||
|
"mandatory_depends_on": "eval:parent.doctype == 'Asset'",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -86,7 +87,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-09-16 12:11:30.631788",
|
"modified": "2020-10-30 15:22:29.119868",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset Finance Book",
|
"name": "Asset Finance Book",
|
||||||
|
@ -876,7 +876,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item",
|
"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item",
|
||||||
"qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[1].name
|
"qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -885,6 +885,10 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
|
se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
|
||||||
se.submit()
|
se.submit()
|
||||||
|
|
||||||
|
# Test po_detail field has value or not
|
||||||
|
for item_row in se.items:
|
||||||
|
self.assertEqual(item_row.po_detail, po.supplied_items[item_row.idx - 1].name)
|
||||||
|
|
||||||
po_doc = frappe.get_doc("Purchase Order", po.name)
|
po_doc = frappe.get_doc("Purchase Order", po.name)
|
||||||
for row in po_doc.supplied_items:
|
for row in po_doc.supplied_items:
|
||||||
# Valid that whether transferred quantity is matching with supplied qty or not in the purchase order
|
# Valid that whether transferred quantity is matching with supplied qty or not in the purchase order
|
||||||
|
@ -263,6 +263,7 @@ class AccountsController(TransactionBase):
|
|||||||
if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
|
if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
|
||||||
parent_dict.update({"customer": parent_dict.get("party_name")})
|
parent_dict.update({"customer": parent_dict.get("party_name")})
|
||||||
|
|
||||||
|
self.pricing_rules = []
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.get("item_code"):
|
if item.get("item_code"):
|
||||||
args = parent_dict.copy()
|
args = parent_dict.copy()
|
||||||
@ -301,6 +302,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
if ret.get("pricing_rules"):
|
if ret.get("pricing_rules"):
|
||||||
self.apply_pricing_rule_on_items(item, ret)
|
self.apply_pricing_rule_on_items(item, ret)
|
||||||
|
self.set_pricing_rule_details(item, ret)
|
||||||
|
|
||||||
if self.doctype == "Purchase Invoice":
|
if self.doctype == "Purchase Invoice":
|
||||||
self.set_expense_account(for_validate)
|
self.set_expense_account(for_validate)
|
||||||
@ -322,6 +324,9 @@ class AccountsController(TransactionBase):
|
|||||||
if item.get('discount_amount'):
|
if item.get('discount_amount'):
|
||||||
item.rate = item.price_list_rate - item.discount_amount
|
item.rate = item.price_list_rate - item.discount_amount
|
||||||
|
|
||||||
|
if item.get("apply_discount_on_discounted_rate") and pricing_rule_args.get("rate"):
|
||||||
|
item.rate = pricing_rule_args.get("rate")
|
||||||
|
|
||||||
elif pricing_rule_args.get('free_item_data'):
|
elif pricing_rule_args.get('free_item_data'):
|
||||||
apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
|
apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
|
||||||
|
|
||||||
@ -335,6 +340,18 @@ class AccountsController(TransactionBase):
|
|||||||
frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}")
|
frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}")
|
||||||
.format(item.idx, frappe.bold(title), frappe.bold(item.item_code)))
|
.format(item.idx, frappe.bold(title), frappe.bold(item.item_code)))
|
||||||
|
|
||||||
|
def set_pricing_rule_details(self, item_row, args):
|
||||||
|
pricing_rules = get_applied_pricing_rules(args.get("pricing_rules"))
|
||||||
|
if not pricing_rules: return
|
||||||
|
|
||||||
|
for pricing_rule in pricing_rules:
|
||||||
|
self.append("pricing_rules", {
|
||||||
|
"pricing_rule": pricing_rule,
|
||||||
|
"item_code": item_row.item_code,
|
||||||
|
"child_docname": item_row.name,
|
||||||
|
"rule_applied": True
|
||||||
|
})
|
||||||
|
|
||||||
def set_taxes(self):
|
def set_taxes(self):
|
||||||
if not self.meta.get_field("taxes"):
|
if not self.meta.get_field("taxes"):
|
||||||
return
|
return
|
||||||
|
@ -301,7 +301,7 @@ class BuyingController(StockController):
|
|||||||
# backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
|
# backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
|
||||||
|
|
||||||
for raw_material in transferred_raw_materials + non_stock_items:
|
for raw_material in transferred_raw_materials + non_stock_items:
|
||||||
rm_item_key = (raw_material.rm_item_code, item.purchase_order)
|
rm_item_key = (raw_material.rm_item_code, item.item_code, item.purchase_order)
|
||||||
raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
|
raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
|
||||||
|
|
||||||
consumed_qty = raw_material_data.get('qty', 0)
|
consumed_qty = raw_material_data.get('qty', 0)
|
||||||
@ -914,7 +914,7 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders):
|
|||||||
purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references)
|
purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references)
|
||||||
|
|
||||||
for data in purchase_receipt_supplied_items:
|
for data in purchase_receipt_supplied_items:
|
||||||
pr_key = (data.rm_item_code, args[0])
|
pr_key = (data.rm_item_code, data.main_item_code, args[0])
|
||||||
if pr_key not in backflushed_raw_materials_map:
|
if pr_key not in backflushed_raw_materials_map:
|
||||||
backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({
|
backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({
|
||||||
"qty": 0.0,
|
"qty": 0.0,
|
||||||
@ -940,7 +940,7 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders):
|
|||||||
|
|
||||||
def get_supplied_items(item_code, purchase_receipt, references):
|
def get_supplied_items(item_code, purchase_receipt, references):
|
||||||
return frappe.get_all("Purchase Receipt Item Supplied",
|
return frappe.get_all("Purchase Receipt Item Supplied",
|
||||||
fields=["rm_item_code", "consumed_qty", "serial_no", "batch_no"],
|
fields=["rm_item_code", "main_item_code", "consumed_qty", "serial_no", "batch_no"],
|
||||||
filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)})
|
filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)})
|
||||||
|
|
||||||
def get_asset_item_details(asset_items):
|
def get_asset_item_details(asset_items):
|
||||||
|
@ -608,16 +608,19 @@ class calculate_taxes_and_totals(object):
|
|||||||
base_rate_with_margin = 0.0
|
base_rate_with_margin = 0.0
|
||||||
if item.price_list_rate:
|
if item.price_list_rate:
|
||||||
if item.pricing_rules and not self.doc.ignore_pricing_rule:
|
if item.pricing_rules and not self.doc.ignore_pricing_rule:
|
||||||
|
has_margin = False
|
||||||
for d in get_applied_pricing_rules(item.pricing_rules):
|
for d in get_applied_pricing_rules(item.pricing_rules):
|
||||||
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
||||||
|
|
||||||
if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\
|
if (pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == self.doc.currency)\
|
||||||
or (pricing_rule.margin_type == 'Percentage'):
|
or (pricing_rule.margin_type == 'Percentage'):
|
||||||
item.margin_type = pricing_rule.margin_type
|
item.margin_type = pricing_rule.margin_type
|
||||||
item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
|
item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
|
||||||
else:
|
has_margin = True
|
||||||
item.margin_type = None
|
|
||||||
item.margin_rate_or_amount = 0.0
|
if not has_margin:
|
||||||
|
item.margin_type = None
|
||||||
|
item.margin_rate_or_amount = 0.0
|
||||||
|
|
||||||
if item.margin_type and item.margin_rate_or_amount:
|
if item.margin_type and item.margin_rate_or_amount:
|
||||||
margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
|
margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
|
||||||
|
@ -75,6 +75,6 @@ class StudentAttendance(Document):
|
|||||||
})
|
})
|
||||||
|
|
||||||
if attendance_record:
|
if attendance_record:
|
||||||
record = get_link_to_form('Attendance Record', attendance_record)
|
record = get_link_to_form('Student Attendance', attendance_record)
|
||||||
frappe.throw(_('Student Attendance record {0} already exists against the Student {1}')
|
frappe.throw(_('Student Attendance record {0} already exists against the Student {1}')
|
||||||
.format(record, frappe.bold(self.student)), title=_('Duplicate Entry'))
|
.format(record, frappe.bold(self.student)), title=_('Duplicate Entry'))
|
||||||
|
@ -56,23 +56,35 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if (frm.doc.docstatus === 1) {
|
if (frm.doc.docstatus === 1) {
|
||||||
frm.trigger("show_progress");
|
frm.trigger("show_progress");
|
||||||
|
|
||||||
|
if (frm.doc.status !== "Completed") {
|
||||||
|
if (frm.doc.po_items && frm.doc.status !== "Closed") {
|
||||||
|
frm.add_custom_button(__("Work Order"), ()=> {
|
||||||
|
frm.trigger("make_work_order");
|
||||||
|
}, __('Create'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frm.doc.mr_items && !in_list(['Material Requested', 'Closed'], frm.doc.status)) {
|
||||||
|
frm.add_custom_button(__("Material Request"), ()=> {
|
||||||
|
frm.trigger("make_material_request");
|
||||||
|
}, __('Create'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frm.doc.status === "Closed") {
|
||||||
|
frm.add_custom_button(__("Re-open"), function() {
|
||||||
|
frm.events.close_open_production_plan(frm, false);
|
||||||
|
}, __("Status"));
|
||||||
|
} else {
|
||||||
|
frm.add_custom_button(__("Close"), function() {
|
||||||
|
frm.events.close_open_production_plan(frm, true);
|
||||||
|
}, __("Status"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.docstatus === 1 && frm.doc.po_items
|
if (frm.doc.status !== "Closed") {
|
||||||
&& frm.doc.status != 'Completed') {
|
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||||
frm.add_custom_button(__("Work Order"), ()=> {
|
|
||||||
frm.trigger("make_work_order");
|
|
||||||
}, __('Create'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.docstatus === 1 && frm.doc.mr_items
|
|
||||||
&& !in_list(['Material Requested', 'Completed'], frm.doc.status)) {
|
|
||||||
frm.add_custom_button(__("Material Request"), ()=> {
|
|
||||||
frm.trigger("make_material_request");
|
|
||||||
}, __('Create'));
|
|
||||||
}
|
|
||||||
|
|
||||||
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
|
||||||
frm.trigger("material_requirement");
|
frm.trigger("material_requirement");
|
||||||
|
|
||||||
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
|
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
|
||||||
@ -121,6 +133,18 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
set_field_options("projected_qty_formula", projected_qty_formula);
|
set_field_options("projected_qty_formula", projected_qty_formula);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
close_open_production_plan: (frm, close=false) => {
|
||||||
|
frappe.call({
|
||||||
|
method: "set_status",
|
||||||
|
freeze: true,
|
||||||
|
doc: frm.doc,
|
||||||
|
args: {close : close},
|
||||||
|
callback: function() {
|
||||||
|
frm.reload_doc();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
make_work_order: function(frm) {
|
make_work_order: function(frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "make_work_order",
|
method: "make_work_order",
|
||||||
|
@ -275,7 +275,7 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled\nMaterial Requested",
|
"options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nClosed\nCancelled\nMaterial Requested",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -304,9 +304,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-calendar",
|
"icon": "fa fa-calendar",
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-02-03 00:25:25.934202",
|
"modified": "2020-10-26 13:00:54.335319",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Plan",
|
"name": "Production Plan",
|
||||||
|
@ -219,13 +219,17 @@ class ProductionPlan(Document):
|
|||||||
filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
|
filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
|
||||||
frappe.delete_doc('Work Order', d.name)
|
frappe.delete_doc('Work Order', d.name)
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self, close=None):
|
||||||
self.status = {
|
self.status = {
|
||||||
0: 'Draft',
|
0: 'Draft',
|
||||||
1: 'Submitted',
|
1: 'Submitted',
|
||||||
2: 'Cancelled'
|
2: 'Cancelled'
|
||||||
}.get(self.docstatus)
|
}.get(self.docstatus)
|
||||||
|
|
||||||
|
if close:
|
||||||
|
self.db_set('status', 'Closed')
|
||||||
|
return
|
||||||
|
|
||||||
if self.total_produced_qty > 0:
|
if self.total_produced_qty > 0:
|
||||||
self.status = "In Process"
|
self.status = "In Process"
|
||||||
if self.total_produced_qty == self.total_planned_qty:
|
if self.total_produced_qty == self.total_planned_qty:
|
||||||
@ -235,6 +239,9 @@ class ProductionPlan(Document):
|
|||||||
self.update_ordered_status()
|
self.update_ordered_status()
|
||||||
self.update_requested_status()
|
self.update_requested_status()
|
||||||
|
|
||||||
|
if close is not None:
|
||||||
|
self.db_set('status', self.status)
|
||||||
|
|
||||||
def update_ordered_status(self):
|
def update_ordered_status(self):
|
||||||
update_status = False
|
update_status = False
|
||||||
for d in self.po_items:
|
for d in self.po_items:
|
||||||
@ -735,10 +742,12 @@ def get_items_for_material_requests(doc, warehouses=None):
|
|||||||
mr_items = new_mr_items
|
mr_items = new_mr_items
|
||||||
|
|
||||||
if not mr_items:
|
if not mr_items:
|
||||||
frappe.msgprint(_("""As raw materials projected quantity is more than required quantity,
|
to_enable = frappe.bold(_("Ignore Existing Projected Quantity"))
|
||||||
there is no need to create material request for the warehouse {0}.
|
warehouse = frappe.bold(doc.get('for_warehouse'))
|
||||||
Still if you want to make material request,
|
message = _("As there are sufficient raw materials, Material Request is not required for Warehouse {0}.").format(warehouse) + "<br><br>"
|
||||||
kindly enable <b>Ignore Existing Projected Quantity</b> checkbox""").format(doc.get('for_warehouse')))
|
message += _(" If you still want to proceed, please enable {0}.").format(to_enable)
|
||||||
|
|
||||||
|
frappe.msgprint(message, title=_("Note"))
|
||||||
|
|
||||||
return mr_items
|
return mr_items
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
frappe.listview_settings['Production Plan'] = {
|
frappe.listview_settings['Production Plan'] = {
|
||||||
add_fields: ["status"],
|
add_fields: ["status"],
|
||||||
filters: [["status", "!=", "Stopped"]],
|
filters: [["status", "!=", "Closed"]],
|
||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
if(doc.status==="Submitted") {
|
if(doc.status==="Submitted") {
|
||||||
return [__("Not Started"), "orange", "status,=,Submitted"];
|
return [__("Not Started"), "orange", "status,=,Submitted"];
|
||||||
@ -10,7 +10,8 @@ frappe.listview_settings['Production Plan'] = {
|
|||||||
"In Process": "orange",
|
"In Process": "orange",
|
||||||
"Completed": "green",
|
"Completed": "green",
|
||||||
"Material Requested": "darkgrey",
|
"Material Requested": "darkgrey",
|
||||||
"Cancelled": "darkgrey"
|
"Cancelled": "darkgrey",
|
||||||
|
"Closed": "grey"
|
||||||
}[doc.status], "status,=," + doc.status];
|
}[doc.status], "status,=," + doc.status];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,30 +11,20 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "warehouse",
|
"fieldname": "warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Warehouse",
|
"label": "Warehouse",
|
||||||
"options": "Warehouse"
|
"options": "Warehouse"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-02-02 10:37:16.650836",
|
"modified": "2020-10-26 12:55:00.778201",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Plan Material Request Warehouse",
|
"name": "Production Plan Material Request Warehouse",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [],
|
||||||
{
|
|
||||||
"create": 1,
|
|
||||||
"delete": 1,
|
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"share": 1,
|
|
||||||
"write": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
@ -732,4 +732,5 @@ erpnext.patches.v13_0.set_youtube_video_id
|
|||||||
erpnext.patches.v13_0.print_uom_after_quantity_patch
|
erpnext.patches.v13_0.print_uom_after_quantity_patch
|
||||||
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
|
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
|
||||||
erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail
|
erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail
|
||||||
erpnext.patches.v13_0.update_returned_qty_in_pr_dn #12am
|
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
|
||||||
|
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
|
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# MIT License. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("hr", "doctype", "employee")
|
||||||
|
|
||||||
|
if frappe.db.has_column("Employee", "reason_for_resignation"):
|
||||||
|
frappe.db.sql(""" UPDATE `tabEmployee`
|
||||||
|
SET reason_for_leaving = reason_for_resignation
|
||||||
|
WHERE status = 'Left' and reason_for_leaving is null and reason_for_resignation is not null
|
||||||
|
""")
|
||||||
|
|
@ -352,9 +352,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
|
|
||||||
let show_description = function(idx, exist = null) {
|
let show_description = function(idx, exist = null) {
|
||||||
if (exist) {
|
if (exist) {
|
||||||
scan_barcode_field.set_new_description(__('Row #{0}: Qty increased by 1', [idx]));
|
frappe.show_alert({
|
||||||
|
message: __('Row #{0}: Qty increased by 1', [idx]),
|
||||||
|
indicator: 'green'
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
scan_barcode_field.set_new_description(__('Row #{0}: Item added', [idx]));
|
frappe.show_alert({
|
||||||
|
message: __('Row #{0}: Item added', [idx]),
|
||||||
|
indicator: 'green'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +371,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}).then(r => {
|
}).then(r => {
|
||||||
const data = r && r.message;
|
const data = r && r.message;
|
||||||
if (!data || Object.keys(data).length === 0) {
|
if (!data || Object.keys(data).length === 0) {
|
||||||
scan_barcode_field.set_new_description(__('Cannot find Item with this barcode'));
|
frappe.show_alert({
|
||||||
|
message: __('Cannot find Item with this Barcode'),
|
||||||
|
indicator: 'red'
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,7 +660,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
let child = frappe.model.add_child(me.frm.doc, "taxes");
|
let child = frappe.model.add_child(me.frm.doc, "taxes");
|
||||||
child.charge_type = "On Net Total";
|
child.charge_type = "On Net Total";
|
||||||
child.account_head = tax;
|
child.account_head = tax;
|
||||||
child.rate = rate;
|
child.rate = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ def get_place_of_supply(party_details, doctype):
|
|||||||
if not frappe.get_meta('Address').has_field('gst_state'): return
|
if not frappe.get_meta('Address').has_field('gst_state'): return
|
||||||
|
|
||||||
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
|
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
|
||||||
address_name = party_details.shipping_address_name or party_details.customer_address
|
address_name = party_details.customer_address or party_details.shipping_address_name
|
||||||
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
|
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
|
||||||
address_name = party_details.shipping_address or party_details.supplier_address
|
address_name = party_details.shipping_address or party_details.supplier_address
|
||||||
|
|
||||||
|
@ -14,11 +14,11 @@ from six import string_types
|
|||||||
def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""):
|
def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""):
|
||||||
data = dict()
|
data = dict()
|
||||||
result = []
|
result = []
|
||||||
warehouse, show_only_available_items = "", False
|
warehouse, hide_unavailable_items = "", False
|
||||||
|
|
||||||
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
|
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
|
||||||
if not allow_negative_stock:
|
if not allow_negative_stock:
|
||||||
warehouse, show_only_available_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'show_only_available_items'])
|
warehouse, hide_unavailable_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items'])
|
||||||
|
|
||||||
if not frappe.db.exists('Item Group', item_group):
|
if not frappe.db.exists('Item Group', item_group):
|
||||||
item_group = get_root_of('Item Group')
|
item_group = get_root_of('Item Group')
|
||||||
@ -48,7 +48,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va
|
|||||||
lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt'])
|
lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt'])
|
||||||
|
|
||||||
bin_join_selection, bin_join_condition = "", ""
|
bin_join_selection, bin_join_condition = "", ""
|
||||||
if show_only_available_items:
|
if hide_unavailable_items:
|
||||||
bin_join_selection = ", `tabBin` bin"
|
bin_join_selection = ", `tabBin` bin"
|
||||||
bin_join_condition = "AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0"
|
bin_join_condition = "AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0"
|
||||||
|
|
||||||
|
@ -615,6 +615,15 @@ class StockEntry(StockController):
|
|||||||
if not row.subcontracted_item:
|
if not row.subcontracted_item:
|
||||||
frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}")
|
frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}")
|
||||||
.format(row.idx, frappe.bold(row.item_code)))
|
.format(row.idx, frappe.bold(row.item_code)))
|
||||||
|
elif not row.po_detail:
|
||||||
|
filters = {
|
||||||
|
"parent": self.purchase_order, "docstatus": 1,
|
||||||
|
"rm_item_code": row.item_code, "main_item_code": row.subcontracted_item
|
||||||
|
}
|
||||||
|
|
||||||
|
po_detail = frappe.db.get_value("Purchase Order Item Supplied", filters, "name")
|
||||||
|
if po_detail:
|
||||||
|
row.db_set("po_detail", po_detail)
|
||||||
|
|
||||||
def validate_bom(self):
|
def validate_bom(self):
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
|
@ -168,6 +168,7 @@ def get_stock_ledger_entries(filters, items):
|
|||||||
from
|
from
|
||||||
`tabStock Ledger Entry` sle force index (posting_sort_index)
|
`tabStock Ledger Entry` sle force index (posting_sort_index)
|
||||||
where sle.docstatus < 2 %s %s
|
where sle.docstatus < 2 %s %s
|
||||||
|
and is_cancelled = 0
|
||||||
order by sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty""" % #nosec
|
order by sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty""" % #nosec
|
||||||
(item_conditions_sql, conditions), as_dict=1)
|
(item_conditions_sql, conditions), as_dict=1)
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import json
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe import utils
|
from frappe import utils
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff
|
from frappe.utils import now_datetime, getdate, get_weekdays, add_to_date, get_time, get_datetime, time_diff_in_seconds
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe.utils.user import is_website_user
|
from frappe.utils.user import is_website_user
|
||||||
@ -355,13 +355,13 @@ def set_service_level_agreement_variance(issue=None):
|
|||||||
doc = frappe.get_doc("Issue", issue.name)
|
doc = frappe.get_doc("Issue", issue.name)
|
||||||
|
|
||||||
if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer
|
if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer
|
||||||
variance = round(time_diff_in_hours(doc.response_by, current_time), 2)
|
variance = round(time_diff_in_seconds(doc.response_by, current_time), 2)
|
||||||
frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False)
|
frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False)
|
||||||
if variance < 0:
|
if variance < 0:
|
||||||
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False)
|
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False)
|
||||||
|
|
||||||
if not doc.resolution_date: # resolution_date set when issue has been closed
|
if not doc.resolution_date: # resolution_date set when issue has been closed
|
||||||
variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2)
|
variance = round(time_diff_in_seconds(doc.resolution_by, current_time), 2)
|
||||||
frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False)
|
frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False)
|
||||||
if variance < 0:
|
if variance < 0:
|
||||||
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False)
|
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False)
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
<p class='lead'>{{ education_settings.description }}</p>
|
<p class='lead'>{{ education_settings.description }}</p>
|
||||||
<p class="mt-4">
|
<p class="mt-4">
|
||||||
{% if frappe.session.user == 'Guest' %}
|
{% if frappe.session.user == 'Guest' %}
|
||||||
<a class="btn btn-primary btn-lg" href="'/login#signup'">{{_('Sign Up')}}</a>
|
<a class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -62,4 +62,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user