feat(marketplace): Add category wise search (#19760)
feat(marketplace): change route for category wise search
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index acfc660..9979377 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -332,6 +332,7 @@
"label": "Reference"
},
{
+ "depends_on": "eval:doc.docstatus==0",
"fieldname": "get_outstanding_invoice",
"fieldtype": "Button",
"label": "Get Outstanding Invoice"
@@ -575,7 +576,7 @@
}
],
"is_submittable": 1,
- "modified": "2019-11-06 12:59:43.151721",
+ "modified": "2019-12-08 13:02:30.016610",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index eda59ab..6133b1c 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -350,13 +350,13 @@
if dt in ["Sales Order", "Purchase Order"]:
grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
- if dt in ["Sales Invoice", "Purchase Invoice"]:
+ elif dt in ["Sales Invoice", "Purchase Invoice"]:
if ref_doc.party_account_currency == ref_doc.currency:
grand_total = flt(ref_doc.outstanding_amount)
else:
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
- if dt == "Fees":
+ elif dt == "Fees":
grand_total = ref_doc.outstanding_amount
if grand_total > 0 :
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 430dce7..e871d98 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -34,8 +34,7 @@
def validate_duplicate_apply_on(self):
field = apply_on_dict.get(self.apply_on)
- values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field)]
-
+ values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field) if field]
if len(values) != len(set(values)):
frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on))
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 3bb3df8..7b2061a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -830,7 +830,11 @@
)
def make_gle_for_rounding_adjustment(self, gl_entries):
- if self.rounding_adjustment:
+ # if rounding adjustment in small and conversion rate is also small then
+ # base_rounding_adjustment may become zero due to small precision
+ # eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
+ # then base_rounding_adjustment becomes zero and error is thrown in GL Entry
+ if self.rounding_adjustment and self.base_rounding_adjustment:
round_off_account, round_off_cost_center = \
get_round_off_account_and_cost_center(self.company)
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 27d8233..acb0398 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "hash",
"creation": "2013-05-22 12:43:10",
"doctype": "DocType",
@@ -507,7 +508,8 @@
"depends_on": "enable_deferred_expense",
"fieldname": "service_stop_date",
"fieldtype": "Date",
- "label": "Service Stop Date"
+ "label": "Service Stop Date",
+ "no_copy": 1
},
{
"default": "0",
@@ -523,13 +525,15 @@
"depends_on": "enable_deferred_expense",
"fieldname": "service_start_date",
"fieldtype": "Date",
- "label": "Service Start Date"
+ "label": "Service Start Date",
+ "no_copy": 1
},
{
"depends_on": "enable_deferred_expense",
"fieldname": "service_end_date",
"fieldtype": "Date",
- "label": "Service End Date"
+ "label": "Service End Date",
+ "no_copy": 1
},
{
"fieldname": "reference",
@@ -766,7 +770,8 @@
],
"idx": 1,
"istable": 1,
- "modified": "2019-11-21 16:27:52.043744",
+ "links": [],
+ "modified": "2019-12-04 12:23:17.046413",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index def671c..c4d64a7 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -538,7 +538,7 @@
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
if (d.item_code and is_stock_item == 1\
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
- msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
+ msgprint(_("{0} is mandatory for Stock Item {1}").format(i,d.item_code), raise_exception=1)
def validate_proj_cust(self):
@@ -953,7 +953,7 @@
)
def make_gle_for_rounding_adjustment(self, gl_entries):
- if flt(self.rounding_adjustment, self.precision("rounding_adjustment")):
+ if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment:
round_off_account, round_off_cost_center = \
get_round_off_account_and_cost_center(self.company)
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 779ac4f..b2294e4 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "hash",
"creation": "2013-06-04 11:02:19",
"doctype": "DocType",
@@ -484,7 +485,8 @@
"depends_on": "enable_deferred_revenue",
"fieldname": "service_stop_date",
"fieldtype": "Date",
- "label": "Service Stop Date"
+ "label": "Service Stop Date",
+ "no_copy": 1
},
{
"default": "0",
@@ -500,13 +502,15 @@
"depends_on": "enable_deferred_revenue",
"fieldname": "service_start_date",
"fieldtype": "Date",
- "label": "Service Start Date"
+ "label": "Service Start Date",
+ "no_copy": 1
},
{
"depends_on": "enable_deferred_revenue",
"fieldname": "service_end_date",
"fieldtype": "Date",
- "label": "Service End Date"
+ "label": "Service End Date",
+ "no_copy": 1
},
{
"collapsible": 1,
@@ -783,7 +787,8 @@
],
"idx": 1,
"istable": 1,
- "modified": "2019-07-16 16:36:46.527606",
+ "links": [],
+ "modified": "2019-12-04 12:22:38.517710",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
index 5f0fdc9..4a9f1b0 100644
--- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
+++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
@@ -88,6 +88,11 @@
"label": __("Supplier Group"),
"fieldtype": "Link",
"options": "Supplier Group"
+ },
+ {
+ "fieldname":"based_on_payment_terms",
+ "label": __("Based On Payment Terms"),
+ "fieldtype": "Check",
}
],
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 41989bf..2c53f6e 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -60,6 +60,7 @@
def get_data(self):
self.get_gl_entries()
+ self.get_sales_invoices_or_customers_based_on_sales_person()
self.voucher_balance = OrderedDict()
self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
@@ -103,12 +104,18 @@
def get_invoices(self, gle):
if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
- self.invoices.add(gle.voucher_no)
+ if self.filters.get("sales_person"):
+ if gle.voucher_no in self.sales_person_records.get("Sales Invoice", []) \
+ or gle.party in self.sales_person_records.get("Customer", []):
+ self.invoices.add(gle.voucher_no)
+ else:
+ self.invoices.add(gle.voucher_no)
def update_voucher_balance(self, gle):
# get the row where this balance needs to be updated
# if its a payment, it will return the linked invoice or will be considered as advance
row = self.get_voucher_balance(gle)
+ if not row: return
# gle_balance will be the total "debit - credit" for receivable type reports and
# and vice-versa for payable type reports
gle_balance = self.get_gle_balance(gle)
@@ -129,8 +136,13 @@
row.paid -= gle_balance
def get_voucher_balance(self, gle):
- voucher_balance = None
+ if self.filters.get("sales_person"):
+ against_voucher = gle.against_voucher or gle.voucher_no
+ if not (gle.party in self.sales_person_records.get("Customer", []) or \
+ against_voucher in self.sales_person_records.get("Sales Invoice", [])):
+ return
+ voucher_balance = None
if gle.against_voucher:
# find invoice
against_voucher = gle.against_voucher
@@ -512,6 +524,22 @@
order by posting_date, party"""
.format(select_fields, conditions), values, as_dict=True)
+ def get_sales_invoices_or_customers_based_on_sales_person(self):
+ if self.filters.get("sales_person"):
+ lft, rgt = frappe.db.get_value("Sales Person",
+ self.filters.get("sales_person"), ["lft", "rgt"])
+
+ records = frappe.db.sql("""
+ select distinct parent, parenttype
+ from `tabSales Team` steam
+ where parenttype in ('Customer', 'Sales Invoice')
+ and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person)
+ """, (lft, rgt), as_dict=1)
+
+ self.sales_person_records = frappe._dict()
+ for d in records:
+ self.sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
+
def prepare_conditions(self):
conditions = [""]
values = [self.party_type, self.filters.report_date]
@@ -564,16 +592,6 @@
conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)")
values.append(self.filters.get("sales_partner"))
- if self.filters.get("sales_person"):
- lft, rgt = frappe.db.get_value("Sales Person",
- self.filters.get("sales_person"), ["lft", "rgt"])
-
- conditions.append("""exists(select name from `tabSales Team` steam where
- steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1})
- and ((steam.parent = voucher_no and steam.parenttype = voucher_type)
- or (steam.parent = against_voucher and steam.parenttype = against_voucher_type)
- or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt))
-
def add_supplier_filters(self, conditions, values):
if self.filters.get("supplier_group"):
conditions.append("""party in (select name from tabSupplier
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
index 0120608..d54824b 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
@@ -106,6 +106,11 @@
"label": __("Sales Person"),
"fieldtype": "Link",
"options": "Sales Person"
+ },
+ {
+ "fieldname":"based_on_payment_terms",
+ "label": __("Based On Payment Terms"),
+ "fieldtype": "Check",
}
],
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index 8955830..b607c0f 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -36,7 +36,7 @@
self.filters.report_date) or {}
for party, party_dict in iteritems(self.party_total):
- if party_dict.outstanding <= 0:
+ if party_dict.outstanding == 0:
continue
row = frappe._dict()
diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
index bd2c34b..3e47906 100644
--- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
+++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
@@ -18,14 +18,17 @@
return columns, data
def get_data(filters, show_party_name):
- party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
+ if filters.get('party_type') in ('Customer', 'Supplier', 'Employee', 'Member'):
+ party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
if filters.get('party_type') == 'Student':
party_name_field = 'first_name'
elif filters.get('party_type') == 'Shareholder':
party_name_field = 'title'
+ else:
+ party_name_field = 'name'
party_filters = {"name": filters.get("party")} if filters.get("party") else {}
- parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field],
+ parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field],
filters = party_filters, order_by="name")
company_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
opening_balances = get_opening_balances(filters)
@@ -70,7 +73,7 @@
# totals
for col in total_row:
total_row[col] += row.get(col)
-
+
row.update({
"currency": company_currency
})
@@ -78,7 +81,7 @@
has_value = False
if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit):
has_value =True
-
+
if cint(filters.show_zero_values) or has_value:
data.append(row)
@@ -94,9 +97,9 @@
def get_opening_balances(filters):
gle = frappe.db.sql("""
- select party, sum(debit) as opening_debit, sum(credit) as opening_credit
+ select party, sum(debit) as opening_debit, sum(credit) as opening_credit
from `tabGL Entry`
- where company=%(company)s
+ where company=%(company)s
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
group by party""", {
@@ -114,11 +117,11 @@
def get_balances_within_period(filters):
gle = frappe.db.sql("""
- select party, sum(debit) as debit, sum(credit) as credit
+ select party, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry`
- where company=%(company)s
+ where company=%(company)s
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
- and posting_date >= %(from_date)s and posting_date <= %(to_date)s
+ and posting_date >= %(from_date)s and posting_date <= %(to_date)s
and ifnull(is_opening, 'No') = 'No'
group by party""", {
"company": filters.company,
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 40f1e1e..d32f834 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -517,15 +517,18 @@
asset.set_status('Out of Order')
def make_post_gl_entry():
- if not is_cwip_accounting_enabled(self.asset_category):
- return
- assets = frappe.db.sql_list(""" select name from `tabAsset`
- where ifnull(booked_fixed_asset, 0) = 0 and available_for_use_date = %s""", nowdate())
+ asset_categories = frappe.db.get_all('Asset Category', fields = ['name', 'enable_cwip_accounting'])
- for asset in assets:
- doc = frappe.get_doc('Asset', asset)
- doc.make_gl_entries()
+ for asset_category in asset_categories:
+ if cint(asset_category.enable_cwip_accounting):
+ assets = frappe.db.sql_list(""" select name from `tabAsset`
+ where asset_category = %s and ifnull(booked_fixed_asset, 0) = 0
+ and available_for_use_date = %s""", (asset_category.name, nowdate()))
+
+ for asset in assets:
+ doc = frappe.get_doc('Asset', asset)
+ doc.make_gl_entries()
def get_asset_naming_series():
meta = frappe.get_meta('Asset')
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
index 426caaa..8c737d0 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
@@ -26,5 +26,11 @@
fieldtype: "Link",
options: "Finance Book"
},
+ {
+ fieldname:"date",
+ label: __("Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.get_today()
+ },
]
};
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
index f395499..57b68b4 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import cstr
+from frappe.utils import cstr, today, flt
def execute(filters=None):
filters = frappe._dict(filters or {})
@@ -86,8 +86,8 @@
"width": 90
},
{
- "label": _("Current Value"),
- "fieldname": "current_value",
+ "label": _("Asset Value"),
+ "fieldname": "asset_value",
"options": "Currency",
"width": 90
},
@@ -114,7 +114,7 @@
data = []
conditions = get_conditions(filters)
- current_value_map = get_finance_book_value_map(filters.finance_book)
+ depreciation_amount_map = get_finance_book_value_map(filters.date, filters.finance_book)
pr_supplier_map = get_purchase_receipt_supplier_map()
pi_supplier_map = get_purchase_invoice_supplier_map()
@@ -125,7 +125,9 @@
"available_for_use_date", "status", "purchase_invoice"])
for asset in assets_record:
- if current_value_map.get(asset.name) is not None:
+ asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \
+ - flt(depreciation_amount_map.get(asset.name))
+ if asset_value:
row = {
"asset_id": asset.name,
"asset_name": asset.asset_name,
@@ -138,19 +140,24 @@
"location": asset.location,
"asset_category": asset.asset_category,
"purchase_date": asset.purchase_date,
- "current_value": current_value_map.get(asset.name)
+ "asset_value": asset_value
}
data.append(row)
return data
-def get_finance_book_value_map(finance_book=''):
+def get_finance_book_value_map(date, finance_book=''):
+ if not date:
+ date = today()
return frappe._dict(frappe.db.sql(''' Select
- parent, value_after_depreciation
- FROM `tabAsset Finance Book`
+ parent, SUM(depreciation_amount)
+ FROM `tabDepreciation Schedule`
WHERE
- parentfield='finance_books'
- AND ifnull(finance_book, '')=%s''', cstr(finance_book)))
+ parentfield='schedules'
+ AND schedule_date<=%s
+ AND journal_entry IS NOT NULL
+ AND ifnull(finance_book, '')=%s
+ GROUP BY parent''', (date, cstr(finance_book))))
def get_purchase_receipt_supplier_map():
return frappe._dict(frappe.db.sql(''' Select
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index a0a1e8e..08f5d8b 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -17,6 +17,8 @@
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.controllers.accounts_controller import update_child_qty_rate
from erpnext.controllers.status_updater import OverAllowanceError
+from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
+
class TestPurchaseOrder(unittest.TestCase):
def test_make_purchase_receipt(self):
@@ -620,6 +622,27 @@
po.save()
self.assertEqual(po.schedule_date, add_days(nowdate(), 2))
+
+ def test_po_optional_blanket_order(self):
+ """
+ Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1.
+ Second Purchase Order should not add on to Blanket Orders Ordered Quantity.
+ """
+
+ bo = make_blanket_order(blanket_order_type = "Purchasing", quantity = 10, rate = 10)
+
+ po = create_purchase_order(item_code= "_Test Item", qty = 5, against_blanket_order = 1)
+ po_doc = frappe.get_doc('Purchase Order', po.get('name'))
+ # To test if the PO has a Blanket Order
+ self.assertTrue(po_doc.items[0].blanket_order)
+
+ po = create_purchase_order(item_code= "_Test Item", qty = 5, against_blanket_order = 0)
+ po_doc = frappe.get_doc('Purchase Order', po.get('name'))
+ # To test if the PO does NOT have a Blanket Order
+ self.assertEqual(po_doc.items[0].blanket_order, None)
+
+
+
def make_pr_against_po(po, received_qty=0):
pr = make_purchase_receipt(po)
@@ -693,7 +716,8 @@
"qty": args.qty or 10,
"rate": args.rate or 500,
"schedule_date": add_days(nowdate(), 1),
- "include_exploded_items": args.get('include_exploded_items', 1)
+ "include_exploded_items": args.get('include_exploded_items', 1),
+ "against_blanket_order": args.against_blanket_order
})
if not args.do_not_save:
po.insert()
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index c409c1f..6768dfa 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "hash",
"creation": "2013-05-24 19:29:06",
"doctype": "DocType",
@@ -67,6 +68,7 @@
"supplier_quotation",
"supplier_quotation_item",
"col_break5",
+ "against_blanket_order",
"blanket_order",
"blanket_order_rate",
"item_group",
@@ -511,6 +513,7 @@
"read_only": 1
},
{
+ "depends_on": "eval:doc.against_blanket_order",
"fieldname": "blanket_order",
"fieldtype": "Link",
"label": "Blanket Order",
@@ -518,6 +521,7 @@
"options": "Blanket Order"
},
{
+ "depends_on": "eval:doc.against_blanket_order",
"fieldname": "blanket_order_rate",
"fieldtype": "Currency",
"label": "Blanket Order Rate",
@@ -703,6 +707,12 @@
},
{
"default": "0",
+ "fieldname": "against_blanket_order",
+ "fieldtype": "Check",
+ "label": "Against Blanket Order"
+ },
+ {
+ "default": "0",
"fetch_from": "item_code.is_fixed_asset",
"fieldname": "is_fixed_asset",
"fieldtype": "Check",
@@ -712,7 +722,8 @@
],
"idx": 1,
"istable": 1,
- "modified": "2019-11-07 17:19:12.090355",
+ "links": [],
+ "modified": "2019-12-06 13:17:12.142799",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",
diff --git a/erpnext/buying/setup_wizard_slide/add_a_few_suppliers/add_a_few_suppliers.json b/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json
similarity index 78%
rename from erpnext/buying/setup_wizard_slide/add_a_few_suppliers/add_a_few_suppliers.json
rename to erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json
index 006d139..d3adcb7 100644
--- a/erpnext/buying/setup_wizard_slide/add_a_few_suppliers/add_a_few_suppliers.json
+++ b/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json
@@ -3,18 +3,18 @@
"app": "ERPNext",
"creation": "2019-11-15 14:45:32.626641",
"docstatus": 0,
- "doctype": "Setup Wizard Slide",
+ "doctype": "Onboarding Slide",
"domains": [],
"help_links": [
{
- "label": "Supplier",
+ "label": "Learn More",
"video_id": "zsrrVDk6VBs"
}
],
"idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/supplier.png",
+ "image_src": "/assets/erpnext/images/illustrations/supplier-onboard.png",
"max_count": 3,
- "modified": "2019-11-26 18:26:25.498325",
+ "modified": "2019-12-03 22:53:50.552445",
"modified_by": "Administrator",
"name": "Add A Few Suppliers",
"owner": "Administrator",
@@ -44,6 +44,5 @@
],
"slide_order": 50,
"slide_title": "Add A Few Suppliers",
- "slide_type": "Create",
- "submit_method": ""
+ "slide_type": "Create"
}
\ No newline at end of file
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 1f8b663..75564af 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -61,7 +61,6 @@
_('{0} is blocked so this transaction cannot proceed'.format(supplier_name)), raise_exception=1)
def validate(self):
-
if not self.get('is_return'):
self.validate_qty_is_not_zero()
@@ -100,11 +99,23 @@
if self.is_return:
self.validate_qty()
+ else:
+ self.validate_deferred_start_and_end_date()
validate_regional(self)
if self.doctype != 'Material Request':
apply_pricing_rule_on_transaction(self)
+ def validate_deferred_start_and_end_date(self):
+ for d in self.items:
+ if d.get("enable_deferred_revenue") or d.get("enable_deferred_expense"):
+ if not (d.service_start_date and d.service_end_date):
+ frappe.throw(_("Row #{0}: Service Start and End Date is required for deferred accounting").format(d.idx))
+ elif getdate(d.service_start_date) > getdate(d.service_end_date):
+ frappe.throw(_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx))
+ elif getdate(self.posting_date) > getdate(d.service_end_date):
+ frappe.throw(_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx))
+
def validate_invoice_documents_schedule(self):
self.validate_payment_schedule_dates()
self.set_due_date()
@@ -415,9 +426,10 @@
return gl_dict
def validate_qty_is_not_zero(self):
- for item in self.items:
- if not item.qty:
- frappe.throw(_("Item quantity can not be zero"))
+ if self.doctype != "Purchase Receipt":
+ for item in self.items:
+ if not item.qty:
+ frappe.throw(_("Item quantity can not be zero"))
def validate_account_currency(self, account, account_currency=None):
valid_currency = [self.company_currency]
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py
index 3050d05..00a4bd1 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -41,7 +41,8 @@
email_campaign_exists = frappe.db.exists("Email Campaign", {
"campaign_name": self.campaign_name,
"recipient": self.recipient,
- "status": ("in", ["In Progress", "Scheduled"])
+ "status": ("in", ["In Progress", "Scheduled"]),
+ "name": ("!=", self.name)
})
if email_campaign_exists:
frappe.throw(_("The Campaign '{0}' already exists for the {1} '{2}'").format(self.campaign_name, self.email_campaign_for, self.recipient))
@@ -78,7 +79,7 @@
comm = make(
doctype = "Email Campaign",
name = email_campaign.name,
- subject = email_template.get("subject"),
+ subject = frappe.render_template(email_template.get("subject"), context),
content = frappe.render_template(email_template.get("response"), context),
sender = sender,
recipients = recipient,
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index 99486fa..2880c80 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -130,10 +130,10 @@
def has_lost_quotation(self):
lost_quotation = frappe.db.sql("""
- select q.name
- from `tabQuotation` q, `tabQuotation Item` qi
- where q.name = qi.parent and q.docstatus=1
- and qi.prevdoc_docname =%s and q.status = 'Lost'
+ select name
+ from `tabQuotation`
+ where docstatus=1
+ and opportunity =%s and status = 'Lost'
""", self.name)
if lost_quotation:
if self.has_active_quotation():
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index 9af5e22..76825ce 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -40,7 +40,7 @@
frappe.throw(_("Student {0} exist against student applicant {1}").format(student[0][0], self.student_applicant))
def after_insert(self):
- if not frappe.get_single('Education Settings').user_creation_skip:
+ if not frappe.get_single('Education Settings').get('user_creation_skip'):
self.create_student_user()
def create_student_user(self):
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 703ec06..242531b 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import getdate, validate_email_address, today, add_years, format_datetime
+from frappe.utils import getdate, validate_email_address, today, add_years, format_datetime, cstr
from frappe.model.naming import set_name_by_naming_series
from frappe import throw, _, scrub
from frappe.permissions import add_user_permission, remove_user_permission, \
@@ -152,8 +152,8 @@
elif self.date_of_retirement and self.date_of_joining and (getdate(self.date_of_retirement) <= getdate(self.date_of_joining)):
throw(_("Date Of Retirement must be greater than Date of Joining"))
- elif self.relieving_date and self.date_of_joining and (getdate(self.relieving_date) <= getdate(self.date_of_joining)):
- throw(_("Relieving Date must be greater than Date of Joining"))
+ elif self.relieving_date and self.date_of_joining and (getdate(self.relieving_date) < getdate(self.date_of_joining)):
+ throw(_("Relieving Date must be greater than or equal to Date of Joining"))
elif self.contract_end_date and self.date_of_joining and (getdate(self.contract_end_date) <= getdate(self.date_of_joining)):
throw(_("Contract End Date must be greater than Date of Joining"))
@@ -218,8 +218,8 @@
def reset_employee_emails_cache(self):
prev_doc = self.get_doc_before_save() or {}
- cell_number = self.get('cell_number')
- prev_number = prev_doc.get('cell_number')
+ cell_number = cstr(self.get('cell_number'))
+ prev_number = cstr(prev_doc.get('cell_number'))
if (cell_number != prev_number or
self.get('user_id') != prev_doc.get('user_id')):
frappe.cache().hdel('employees_with_number', cell_number)
diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.js b/erpnext/hr/doctype/payroll_entry/payroll_entry.js
index adc0671..d25eb6d 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.js
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.js
@@ -31,7 +31,11 @@
}
if ((frm.doc.employees || []).length) {
frm.page.set_primary_action(__('Create Salary Slips'), () => {
- frm.save('Submit');
+ frm.save('Submit').then(()=>{
+ frm.page.clear_primary_action();
+ frm.refresh();
+ frm.events.refresh(frm);
+ });
});
}
}
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index faed707..38118bd 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -44,6 +44,8 @@
target.item_name = item.get("item_name")
target.description = item.get("description")
target.uom = item.get("stock_uom")
+ target.against_blanket_order = 1
+ target.blanket_order = source_name
target_doc = get_mapped_doc("Blanket Order", source_name, {
"Blanket Order": {
@@ -71,6 +73,8 @@
target.description = item.get("description")
target.uom = item.get("stock_uom")
target.warehouse = item.get("default_warehouse")
+ target.against_blanket_order = 1
+ target.blanket_order = source_name
target_doc = get_mapped_doc("Blanket Order", source_name, {
"Blanket Order": {
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 5579954..e3ece56 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -606,6 +606,7 @@
item.image,
bom.project,
item.stock_uom,
+ item.item_group,
item.allow_alternative_item,
item_default.default_warehouse,
item_default.expense_account as expense_account,
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json
index 39c5cce..2c21702 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.json
+++ b/erpnext/manufacturing/doctype/job_card/job_card.json
@@ -1,1071 +1,299 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "PO-JOB.#####",
- "beta": 0,
- "creation": "2018-07-09 17:23:29.518745",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "autoname": "naming_series:",
+ "creation": "2018-07-09 17:23:29.518745",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "naming_series",
+ "work_order",
+ "bom_no",
+ "workstation",
+ "operation",
+ "column_break_4",
+ "posting_date",
+ "company",
+ "for_quantity",
+ "wip_warehouse",
+ "timing_detail",
+ "employee",
+ "time_logs",
+ "section_break_13",
+ "total_completed_qty",
+ "total_time_in_mins",
+ "column_break_15",
+ "section_break_8",
+ "items",
+ "more_information",
+ "operation_id",
+ "transferred_qty",
+ "requested_qty",
+ "project",
+ "remarks",
+ "column_break_20",
+ "status",
+ "job_started",
+ "started_time",
+ "amended_from"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "work_order",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Work Order",
- "length": 0,
- "no_copy": 0,
- "options": "Work Order",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 1,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "work_order",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Work Order",
+ "options": "Work Order",
+ "read_only": 1,
+ "reqd": 1,
+ "search_index": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "bom_no",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "BOM No",
- "length": 0,
- "no_copy": 0,
- "options": "BOM",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "bom_no",
+ "fieldtype": "Link",
+ "label": "BOM No",
+ "options": "BOM",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "workstation",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Workstation",
- "length": 0,
- "no_copy": 0,
- "options": "Workstation",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "workstation",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Workstation",
+ "options": "Workstation",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "operation",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Operation",
- "length": 0,
- "no_copy": 0,
- "options": "Operation",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "operation",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Operation",
+ "options": "Operation",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "column_break_4",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Today",
- "fetch_if_empty": 0,
- "fieldname": "posting_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Posting Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "Today",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "label": "Posting Date"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Company",
- "length": 0,
- "no_copy": 0,
- "options": "Company",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "for_quantity",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "For Quantity",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "for_quantity",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "For Quantity",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "wip_warehouse",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "WIP Warehouse",
- "length": 0,
- "no_copy": 0,
- "options": "Warehouse",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "wip_warehouse",
+ "fieldtype": "Link",
+ "label": "WIP Warehouse",
+ "options": "Warehouse",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "timing_detail",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Timing Detail",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "timing_detail",
+ "fieldtype": "Section Break",
+ "label": "Timing Detail"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "employee",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Employee",
- "length": 0,
- "no_copy": 0,
- "options": "Employee",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "label": "Employee",
+ "options": "Employee"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "time_logs",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Time Logs",
- "length": 0,
- "no_copy": 0,
- "options": "Job Card Time Log",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "time_logs",
+ "fieldtype": "Table",
+ "label": "Time Logs",
+ "options": "Job Card Time Log"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "section_break_13",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "section_break_13",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "total_completed_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Total Completed Qty",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "total_completed_qty",
+ "fieldtype": "Float",
+ "label": "Total Completed Qty",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "column_break_15",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_15",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "total_time_in_mins",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Total Time in Mins",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "total_time_in_mins",
+ "fieldtype": "Float",
+ "label": "Total Time in Mins",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "section_break_8",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Raw Materials",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break",
+ "label": "Raw Materials"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "items",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Items",
- "length": 0,
- "no_copy": 0,
- "options": "Job Card Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "label": "Items",
+ "options": "Job Card Item",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 1,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "more_information",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "More Information",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "collapsible": 1,
+ "fieldname": "more_information",
+ "fieldtype": "Section Break",
+ "label": "More Information"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "operation_id",
- "fieldtype": "Data",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Operation ID",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "operation_id",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Operation ID",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "fetch_if_empty": 0,
- "fieldname": "transferred_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Transferred Qty",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "transferred_qty",
+ "fieldtype": "Float",
+ "label": "Transferred Qty",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "fetch_if_empty": 0,
- "fieldname": "requested_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Requested Qty",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "requested_qty",
+ "fieldtype": "Float",
+ "label": "Requested Qty",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "project",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Project",
- "length": 0,
- "no_copy": 0,
- "options": "Project",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "label": "Project",
+ "options": "Project"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "remarks",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Remarks",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "remarks",
+ "fieldtype": "Small Text",
+ "label": "Remarks"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "column_break_20",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_20",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Open",
- "fetch_if_empty": 0,
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Status",
- "length": 0,
- "no_copy": 1,
- "options": "Open\nWork In Progress\nMaterial Transferred\nSubmitted\nCancelled\nCompleted",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "Open",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "label": "Status",
+ "no_copy": 1,
+ "options": "Open\nWork In Progress\nMaterial Transferred\nSubmitted\nCancelled\nCompleted",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "job_started",
- "fieldtype": "Check",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Job Started",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "job_started",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Job Started",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "started_time",
- "fieldtype": "Datetime",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Started Time",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "started_time",
+ "fieldtype": "Datetime",
+ "hidden": 1,
+ "label": "Started Time",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Amended From",
- "length": 0,
- "no_copy": 1,
- "options": "Job Card",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Job Card",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "default": "PO-JOB.#####",
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Naming Series",
+ "options": "PO-JOB.#####",
+ "reqd": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-03-10 17:38:37.499871",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "Job Card",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "is_submittable": 1,
+ "modified": "2019-10-30 01:49:19.606178",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Job Card",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Manufacturing User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Manufacturing User",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Manufacturing Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Manufacturing Manager",
+ "share": 1,
+ "submit": 1,
"write": 1
}
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "operation",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "operation",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 9d2e620..e8787ed 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -4,10 +4,16 @@
from __future__ import unicode_literals
import frappe
+import datetime
from frappe import _
-from frappe.utils import flt, time_diff_in_hours, get_datetime
from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
+from frappe.utils import (flt, cint, time_diff_in_hours, get_datetime, getdate,
+ get_time, add_to_date, time_diff, add_days, get_datetime_str)
+
+from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
+
+class OverlapError(frappe.ValidationError): pass
class JobCard(Document):
def validate(self):
@@ -26,7 +32,7 @@
data = self.get_overlap_for(d)
if data:
frappe.throw(_("Row {0}: From Time and To Time of {1} is overlapping with {2}")
- .format(d.idx, self.name, data.name))
+ .format(d.idx, self.name, data.name), OverlapError)
if d.from_time and d.to_time:
d.time_in_mins = time_diff_in_hours(d.to_time, d.from_time) * 60
@@ -35,27 +41,120 @@
if d.completed_qty:
self.total_completed_qty += d.completed_qty
- def get_overlap_for(self, args):
- existing = frappe.db.sql("""select jc.name as name from
+ def get_overlap_for(self, args, check_next_available_slot=False):
+ production_capacity = 1
+
+ if self.workstation:
+ production_capacity = frappe.get_cached_value("Workstation",
+ self.workstation, 'production_capacity') or 1
+ validate_overlap_for = " and jc.workstation = %(workstation)s "
+
+ if self.employee:
+ # override capacity for employee
+ production_capacity = 1
+ validate_overlap_for = " and jc.employee = %(employee)s "
+
+ extra_cond = ''
+ if check_next_available_slot:
+ extra_cond = " or (%(from_time)s <= jctl.from_time and %(to_time)s <= jctl.to_time)"
+
+ existing = frappe.db.sql("""select jc.name as name, jctl.to_time from
`tabJob Card Time Log` jctl, `tabJob Card` jc where jctl.parent = jc.name and
(
(%(from_time)s > jctl.from_time and %(from_time)s < jctl.to_time) or
(%(to_time)s > jctl.from_time and %(to_time)s < jctl.to_time) or
- (%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time))
- and jctl.name!=%(name)s
- and jc.name!=%(parent)s
- and jc.docstatus < 2
- and jc.employee = %(employee)s """,
+ (%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time) {0}
+ )
+ and jctl.name != %(name)s and jc.name != %(parent)s and jc.docstatus < 2 {1}
+ order by jctl.to_time desc limit 1""".format(extra_cond, validate_overlap_for),
{
"from_time": args.from_time,
"to_time": args.to_time,
"name": args.name or "No Name",
"parent": args.parent or "No Name",
- "employee": self.employee
+ "employee": self.employee,
+ "workstation": self.workstation
}, as_dict=True)
+ if existing and production_capacity > len(existing):
+ return
+
return existing[0] if existing else None
+ def schedule_time_logs(self, row):
+ row.remaining_time_in_mins = row.time_in_mins
+ while row.remaining_time_in_mins > 0:
+ args = frappe._dict({
+ "from_time": row.planned_start_time,
+ "to_time": row.planned_end_time
+ })
+
+ self.validate_overlap_for_workstation(args, row)
+ self.check_workstation_time(row)
+
+ def validate_overlap_for_workstation(self, args, row):
+ # get the last record based on the to time from the job card
+ data = self.get_overlap_for(args, check_next_available_slot=True)
+ if data:
+ row.planned_start_time = get_datetime(data.to_time + get_mins_between_operations())
+
+ def check_workstation_time(self, row):
+ workstation_doc = frappe.get_cached_doc("Workstation", self.workstation)
+ if (not workstation_doc.working_hours or
+ cint(frappe.db.get_single_value("Manufacturing Settings", "allow_overtime"))):
+ row.remaining_time_in_mins -= time_diff_in_minutes(row.planned_end_time,
+ row.planned_start_time)
+
+ self.update_time_logs(row)
+ return
+
+ start_date = getdate(row.planned_start_time)
+ start_time = get_time(row.planned_start_time)
+
+ new_start_date = workstation_doc.validate_workstation_holiday(start_date)
+
+ if new_start_date != start_date:
+ row.planned_start_time = datetime.datetime.combine(new_start_date, start_time)
+ start_date = new_start_date
+
+ total_idx = len(workstation_doc.working_hours)
+
+ for i, time_slot in enumerate(workstation_doc.working_hours):
+ workstation_start_time = datetime.datetime.combine(start_date, get_time(time_slot.start_time))
+ workstation_end_time = datetime.datetime.combine(start_date, get_time(time_slot.end_time))
+
+ if (get_datetime(row.planned_start_time) >= workstation_start_time and
+ get_datetime(row.planned_start_time) <= workstation_end_time):
+ time_in_mins = time_diff_in_minutes(workstation_end_time, row.planned_start_time)
+
+ # If remaining time fit in workstation time logs else split hours as per workstation time
+ if time_in_mins > row.remaining_time_in_mins:
+ row.planned_end_time = add_to_date(row.planned_start_time,
+ minutes=row.remaining_time_in_mins)
+ row.remaining_time_in_mins = 0
+ else:
+ row.planned_end_time = add_to_date(row.planned_start_time, minutes=time_in_mins)
+ row.remaining_time_in_mins -= time_in_mins
+
+ self.update_time_logs(row)
+
+ if total_idx != (i+1) and row.remaining_time_in_mins > 0:
+ row.planned_start_time = datetime.datetime.combine(start_date,
+ get_time(workstation_doc.working_hours[i+1].start_time))
+
+ if row.remaining_time_in_mins > 0:
+ start_date = add_days(start_date, 1)
+ row.planned_start_time = datetime.datetime.combine(start_date,
+ get_time(workstation_doc.working_hours[0].start_time))
+
+ def update_time_logs(self, row):
+ self.append("time_logs", {
+ "from_time": row.planned_start_time,
+ "to_time": row.planned_end_time,
+ "completed_qty": 0,
+ "time_in_mins": time_diff_in_minutes(row.planned_end_time, row.planned_start_time),
+ })
+
def get_required_items(self):
if not self.get('work_order'):
return
@@ -251,3 +350,6 @@
}, target_doc, set_missing_values)
return doclist
+
+def time_diff_in_minutes(string_ed_date, string_st_date):
+ return time_diff(string_ed_date, string_st_date).total_seconds() / 60
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
index 461b9ab..86fa7a8 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
@@ -1,585 +1,178 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2014-11-27 14:12:07.542534",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "creation": "2014-11-27 14:12:07.542534",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+ "raw_materials_consumption_section",
+ "material_consumption",
+ "column_break_3",
+ "backflush_raw_materials_based_on",
+ "capacity_planning",
+ "disable_capacity_planning",
+ "allow_overtime",
+ "allow_production_on_holidays",
+ "column_break_5",
+ "capacity_planning_for_days",
+ "mins_between_operations",
+ "section_break_6",
+ "default_wip_warehouse",
+ "default_fg_warehouse",
+ "column_break_11",
+ "default_scrap_warehouse",
+ "over_production_for_sales_and_work_order_section",
+ "overproduction_percentage_for_sales_order",
+ "column_break_16",
+ "overproduction_percentage_for_work_order",
+ "other_settings_section",
+ "update_bom_costs_automatically"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "capacity_planning",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Capacity Planning",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "capacity_planning",
+ "fieldtype": "Section Break",
+ "label": "Capacity Planning"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Disables creation of time logs against Work Orders. Operations shall not be tracked against Work Order",
- "fieldname": "disable_capacity_planning",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Disable Capacity Planning and Time Tracking",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "depends_on": "eval:!doc.disable_capacity_planning",
+ "description": "Plan time logs outside Workstation Working Hours.",
+ "fieldname": "allow_overtime",
+ "fieldtype": "Check",
+ "label": "Allow Overtime"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Plan time logs outside Workstation Working Hours.",
- "fieldname": "allow_overtime",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Allow Overtime",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "depends_on": "eval:!doc.disable_capacity_planning",
+ "fieldname": "allow_production_on_holidays",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "label": "Allow Production on Holidays"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "allow_production_on_holidays",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Allow Production on Holidays",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "30",
+ "depends_on": "eval:!doc.disable_capacity_planning",
+ "description": "Try planning operations for X days in advance.",
+ "fieldname": "capacity_planning_for_days",
+ "fieldtype": "Int",
+ "label": "Capacity Planning For (Days)"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "30",
- "description": "Try planning operations for X days in advance.",
- "fieldname": "capacity_planning_for_days",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Capacity Planning For (Days)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "eval:!doc.disable_capacity_planning",
+ "description": "Default 10 mins",
+ "fieldname": "mins_between_operations",
+ "fieldtype": "Int",
+ "label": "Time Between Operations (in mins)"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Default 10 mins",
- "fieldname": "mins_between_operations",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Time Between Operations (in mins)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "label": "Default Warehouses for Production"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_6",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "overproduction_percentage_for_sales_order",
+ "fieldtype": "Percent",
+ "label": "Overproduction Percentage For Sales Order"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "overproduction_percentage_for_sales_order",
- "fieldtype": "Percent",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Overproduction Percentage For Sales Order",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "overproduction_percentage_for_work_order",
+ "fieldtype": "Percent",
+ "label": "Overproduction Percentage For Work Order"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "overproduction_percentage_for_work_order",
- "fieldtype": "Percent",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Overproduction Percentage For Work Order",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "BOM",
+ "fieldname": "backflush_raw_materials_based_on",
+ "fieldtype": "Select",
+ "label": "Backflush Raw Materials Based On",
+ "options": "BOM\nMaterial Transferred for Manufacture"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "BOM",
- "fieldname": "backflush_raw_materials_based_on",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Backflush Raw Materials Based On",
- "length": 0,
- "no_copy": 0,
- "options": "BOM\nMaterial Transferred for Manufacture",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "description": "Allow multiple Material Consumption against a Work Order",
+ "fieldname": "material_consumption",
+ "fieldtype": "Check",
+ "label": "Allow Multiple Material Consumption"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Allow multiple Material Consumption against a Work Order",
- "fieldname": "material_consumption",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Allow Multiple Material Consumption",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "description": "Update BOM cost automatically via Scheduler, based on latest valuation rate / price list rate / last purchase rate of raw materials.",
+ "fieldname": "update_bom_costs_automatically",
+ "fieldtype": "Check",
+ "label": "Update BOM Cost Automatically"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Update BOM cost automatically via Scheduler, based on latest valuation rate / price list rate / last purchase rate of raw materials.",
- "fieldname": "update_bom_costs_automatically",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Update BOM Cost Automatically",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_11",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "default_wip_warehouse",
+ "fieldtype": "Link",
+ "label": "Default Work In Progress Warehouse",
+ "options": "Warehouse"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "default_wip_warehouse",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Default Work In Progress Warehouse",
- "length": 0,
- "no_copy": 0,
- "options": "Warehouse",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "default_fg_warehouse",
+ "fieldtype": "Link",
+ "label": "Default Finished Goods Warehouse",
+ "options": "Warehouse"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "default_fg_warehouse",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Default Finished Goods Warehouse",
- "length": 0,
- "no_copy": 0,
- "options": "Warehouse",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "default": "0",
+ "fieldname": "disable_capacity_planning",
+ "fieldtype": "Check",
+ "label": "Disable Capacity Planning"
+ },
+ {
+ "fieldname": "default_scrap_warehouse",
+ "fieldtype": "Link",
+ "label": "Default Scrap Warehouse",
+ "options": "Warehouse"
+ },
+ {
+ "fieldname": "over_production_for_sales_and_work_order_section",
+ "fieldtype": "Section Break",
+ "label": "Over Production for Sales and Work Order"
+ },
+ {
+ "fieldname": "raw_materials_consumption_section",
+ "fieldtype": "Section Break",
+ "label": "Raw Materials Consumption"
+ },
+ {
+ "fieldname": "column_break_16",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "other_settings_section",
+ "fieldtype": "Section Break",
+ "label": "Other Settings"
+ },
+ {
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "icon-wrench",
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "menu_index": 0,
- "modified": "2018-05-28 00:46:25.310621",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "Manufacturing Settings",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "icon": "icon-wrench",
+ "issingle": 1,
+ "modified": "2019-11-26 13:10:45.569341",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Manufacturing Settings",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Manufacturing Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "read": 1,
+ "role": "Manufacturing Manager",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 25c385f..8876253 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -615,6 +615,9 @@
doc['mr_items'] = []
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
+ if not po_items:
+ frappe.throw(_("Items are required to pull the raw materials which is associated with it."))
+
company = doc.get('company')
warehouse = doc.get('for_warehouse')
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index ea2e7a9..0a8f41f 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -5,10 +5,10 @@
from __future__ import unicode_literals
import unittest
import frappe
-from frappe.utils import flt, time_diff_in_hours, now, add_days, cint
+from frappe.utils import flt, time_diff_in_hours, now, add_months, cint, today
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
-from erpnext.manufacturing.doctype.work_order.work_order \
- import make_stock_entry, ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError
+from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry,
+ ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError, CapacityError)
from erpnext.stock.doctype.stock_entry import test_stock_entry
from erpnext.stock.utils import get_bin
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
@@ -307,14 +307,50 @@
{'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
if data:
+ frappe.db.set_value("Manufacturing Settings",
+ None, "disable_capacity_planning", 0)
+
bom, bom_item = data
bom_doc = frappe.get_doc('BOM', bom)
work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
+ self.assertTrue(work_order.planned_end_date)
job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
self.assertEqual(len(job_cards), len(bom_doc.operations))
+ def test_capcity_planning(self):
+ frappe.db.set_value("Manufacturing Settings", None, {
+ "disable_capacity_planning": 0,
+ "capacity_planning_for_days": 1
+ })
+
+ data = frappe.get_cached_value('BOM', {'docstatus': 1, 'item': '_Test FG Item 2',
+ 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
+
+ if data:
+ bom, bom_item = data
+
+ planned_start_date = add_months(today(), months=-1)
+ work_order = make_wo_order_test_record(item=bom_item,
+ qty=10, bom_no=bom, planned_start_date=planned_start_date)
+
+ work_order1 = make_wo_order_test_record(item=bom_item,
+ qty=30, bom_no=bom, planned_start_date=planned_start_date, do_not_submit=1)
+
+ self.assertRaises(CapacityError, work_order1.submit)
+
+ frappe.db.set_value("Manufacturing Settings", None, {
+ "capacity_planning_for_days": 30
+ })
+
+ work_order1.reload()
+ work_order1.submit()
+ self.assertTrue(work_order1.docstatus, 1)
+
+ work_order1.cancel()
+ work_order.cancel()
+
def test_work_order_with_non_transfer_item(self):
items = {'Finished Good Transfer Item': 1, '_Test FG Item': 1, '_Test FG Item 1': 0}
for item, allow_transfer in items.items():
@@ -371,14 +407,12 @@
wo_order.skip_transfer=1
wo_order.get_items_and_operations_from_bom()
wo_order.sales_order = args.sales_order or None
+ wo_order.planned_start_date = args.planned_start_date or now()
if args.source_warehouse:
for item in wo_order.get("required_items"):
item.source_warehouse = args.source_warehouse
- if args.planned_start_date:
- wo_order.planned_start_date = args.planned_start_date
-
if not args.do_not_save:
wo_order.insert()
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 107c79b..5c721c7 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -551,6 +551,7 @@
if (!r.exe) {
frm.set_value("wip_warehouse", r.message.wip_warehouse);
frm.set_value("fg_warehouse", r.message.fg_warehouse);
+ frm.set_value("scrap_warehouse", r.message.scrap_warehouse);
}
}
});
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 2c16bbe..ff48954 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -12,7 +12,7 @@
from dateutil.relativedelta import relativedelta
from erpnext.stock.doctype.item.item import validate_end_of_life
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
-from erpnext.projects.doctype.timesheet.timesheet import OverlapError
+from erpnext.manufacturing.doctype.job_card.job_card import OverlapError
from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
@@ -22,6 +22,7 @@
from frappe.model.mapper import get_mapped_doc
class OverProductionError(frappe.ValidationError): pass
+class CapacityError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass
class OperationTooLongError(frappe.ValidationError): pass
class ItemHasVariantError(frappe.ValidationError): pass
@@ -260,12 +261,50 @@
self.update_reserved_qty_for_production()
def create_job_card(self):
- for row in self.operations:
+ manufacturing_settings_doc = frappe.get_doc("Manufacturing Settings")
+
+ enable_capacity_planning = not cint(manufacturing_settings_doc.disable_capacity_planning)
+ plan_days = cint(manufacturing_settings_doc.capacity_planning_for_days) or 30
+
+ for i, row in enumerate(self.operations):
+ self.set_operation_start_end_time(i, row)
+
if not row.workstation:
frappe.throw(_("Row {0}: select the workstation against the operation {1}")
.format(row.idx, row.operation))
- create_job_card(self, row, auto_create=True)
+ original_start_time = row.planned_start_time
+ job_card_doc = create_job_card(self, row,
+ enable_capacity_planning=enable_capacity_planning, auto_create=True)
+
+ if enable_capacity_planning and job_card_doc:
+ row.planned_start_time = job_card_doc.time_logs[0].from_time
+ row.planned_end_time = job_card_doc.time_logs[-1].to_time
+
+ if date_diff(row.planned_start_time, original_start_time) > plan_days:
+ frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.")
+ .format(plan_days, row.operation), CapacityError)
+
+ row.db_update()
+
+ planned_end_date = self.operations and self.operations[-1].planned_end_time
+ if planned_end_date:
+ self.db_set("planned_end_date", planned_end_date)
+
+ def set_operation_start_end_time(self, idx, row):
+ """Set start and end time for given operation. If first operation, set start as
+ `planned_start_date`, else add time diff to end time of earlier operation."""
+ if idx==0:
+ # first operation at planned_start date
+ row.planned_start_time = self.planned_start_date
+ else:
+ row.planned_start_time = get_datetime(self.operations[idx-1].planned_end_time)\
+ + get_mins_between_operations()
+
+ row.planned_end_time = get_datetime(row.planned_start_time) + relativedelta(minutes = row.time_in_mins)
+
+ if row.planned_start_time == row.planned_end_time:
+ frappe.throw(_("Capacity Planning Error, planned start time can not be same as end time"))
def validate_cancel(self):
if self.status == "Stopped":
@@ -327,9 +366,8 @@
"""Fetch operations from BOM and set in 'Work Order'"""
self.set('operations', [])
- if not self.bom_no \
- or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")):
- return
+ if not self.bom_no:
+ return
if self.use_multi_level_bom:
bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree()
@@ -681,11 +719,13 @@
@frappe.whitelist()
def get_default_warehouse():
- wip_warehouse = frappe.db.get_single_value("Manufacturing Settings",
- "default_wip_warehouse")
- fg_warehouse = frappe.db.get_single_value("Manufacturing Settings",
- "default_fg_warehouse")
- return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse}
+ doc = frappe.get_cached_doc("Manufacturing Settings")
+
+ return {
+ "wip_warehouse": doc.default_wip_warehouse,
+ "fg_warehouse": doc.default_fg_warehouse,
+ "scrap_warehouse": doc.default_scrap_warehouse
+ }
@frappe.whitelist()
def stop_unstop(work_order, status):
@@ -721,7 +761,7 @@
if row:
return create_job_card(work_order, row, qty)
-def create_job_card(work_order, row, qty=0, auto_create=False):
+def create_job_card(work_order, row, qty=0, enable_capacity_planning=False, auto_create=False):
doc = frappe.new_doc("Job Card")
doc.update({
'work_order': work_order.name,
@@ -741,6 +781,9 @@
if auto_create:
doc.flags.ignore_mandatory = True
+ if enable_capacity_planning:
+ doc.schedule_time_logs(row)
+
doc.insert()
frappe.msgprint(_("Job card {0} created").format(doc.name))
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.json b/erpnext/manufacturing/doctype/workstation/workstation.json
index dca9891..d130391 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.json
+++ b/erpnext/manufacturing/doctype/workstation/workstation.json
@@ -1,466 +1,159 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:workstation_name",
- "beta": 0,
- "creation": "2013-01-10 16:34:17",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 0,
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "field:workstation_name",
+ "creation": "2013-01-10 16:34:17",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "field_order": [
+ "workstation_name",
+ "production_capacity",
+ "column_break_3",
+ "over_heads",
+ "hour_rate_electricity",
+ "hour_rate_consumable",
+ "column_break_11",
+ "hour_rate_rent",
+ "hour_rate_labour",
+ "hour_rate",
+ "working_hours_section",
+ "holiday_list",
+ "working_hours",
+ "workstaion_description",
+ "description"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 1,
- "columns": 0,
- "fieldname": "description_section",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Description",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "workstation_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Workstation Name",
+ "oldfieldname": "workstation_name",
+ "oldfieldtype": "Data",
+ "reqd": 1,
+ "unique": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "workstation_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Workstation Name",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "workstation_name",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "description",
- "fieldtype": "Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Description",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "description",
- "oldfieldtype": "Text",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0,
+ "fieldname": "description",
+ "fieldtype": "Text",
+ "in_list_view": 1,
+ "label": "Description",
+ "oldfieldname": "description",
+ "oldfieldtype": "Text",
"width": "300px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "over_heads",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Operating Costs",
- "length": 0,
- "no_copy": 0,
- "oldfieldtype": "Section Break",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "over_heads",
+ "fieldtype": "Section Break",
+ "label": "Operating Costs",
+ "oldfieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "per hour",
- "fieldname": "hour_rate_electricity",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Electricity Cost",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "hour_rate_electricity",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "bold": 1,
+ "description": "per hour",
+ "fieldname": "hour_rate_electricity",
+ "fieldtype": "Currency",
+ "label": "Electricity Cost",
+ "oldfieldname": "hour_rate_electricity",
+ "oldfieldtype": "Currency"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "per hour",
- "fieldname": "hour_rate_consumable",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Consumable Cost",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "hour_rate_consumable",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "bold": 1,
+ "description": "per hour",
+ "fieldname": "hour_rate_consumable",
+ "fieldtype": "Currency",
+ "label": "Consumable Cost",
+ "oldfieldname": "hour_rate_consumable",
+ "oldfieldtype": "Currency"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_11",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "per hour",
- "fieldname": "hour_rate_rent",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Rent Cost",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "hour_rate_rent",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "bold": 1,
+ "description": "per hour",
+ "fieldname": "hour_rate_rent",
+ "fieldtype": "Currency",
+ "label": "Rent Cost",
+ "oldfieldname": "hour_rate_rent",
+ "oldfieldtype": "Currency"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Wages per hour",
- "fieldname": "hour_rate_labour",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Wages",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "hour_rate_labour",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "bold": 1,
+ "description": "Wages per hour",
+ "fieldname": "hour_rate_labour",
+ "fieldtype": "Currency",
+ "label": "Wages",
+ "oldfieldname": "hour_rate_labour",
+ "oldfieldtype": "Currency"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "per hour",
- "fieldname": "hour_rate",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Net Hour Rate",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "hour_rate",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "description": "per hour",
+ "fieldname": "hour_rate",
+ "fieldtype": "Currency",
+ "label": "Net Hour Rate",
+ "oldfieldname": "hour_rate",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "working_hours_section",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Working Hours",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "working_hours_section",
+ "fieldtype": "Section Break",
+ "label": "Working Hours"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "working_hours",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Working Hours",
- "length": 0,
- "no_copy": 0,
- "options": "Workstation Working Hour",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "working_hours",
+ "fieldtype": "Table",
+ "label": "Working Hours",
+ "options": "Workstation Working Hour"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "holiday_list",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Holiday List",
- "length": 0,
- "no_copy": 0,
- "options": "Holiday List",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "fieldname": "holiday_list",
+ "fieldtype": "Link",
+ "label": "Holiday List",
+ "options": "Holiday List"
+ },
+ {
+ "default": "1",
+ "fieldname": "production_capacity",
+ "fieldtype": "Int",
+ "label": "Production Capacity",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "workstaion_description",
+ "fieldtype": "Section Break",
+ "label": "Description"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "icon-wrench",
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2017-07-18 22:28:50.163219",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "Workstation",
- "owner": "Administrator",
+ ],
+ "icon": "icon-wrench",
+ "idx": 1,
+ "modified": "2019-11-26 12:39:19.742052",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Workstation",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Manufacturing User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Manufacturing User",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 1,
- "sort_order": "ASC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "quick_entry": 1,
+ "show_name_in_global_search": 1,
+ "sort_order": "ASC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py
index f6ab72a..3512e59 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation.py
@@ -4,7 +4,9 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import flt, cint, getdate, formatdate, comma_and, time_diff_in_seconds, to_timedelta
+from erpnext.support.doctype.issue.issue import get_holidays
+from frappe.utils import (flt, cint, getdate, formatdate,
+ comma_and, time_diff_in_seconds, to_timedelta, add_days)
from frappe.model.document import Document
from dateutil.parser import parse
@@ -43,6 +45,17 @@
where parent = %s and workstation = %s""",
(self.hour_rate, bom_no[0], self.name))
+ def validate_workstation_holiday(self, schedule_date, skip_holiday_list_check=False):
+ if not skip_holiday_list_check and (not self.holiday_list or
+ cint(frappe.db.get_single_value("Manufacturing Settings", "allow_production_on_holidays"))):
+ return schedule_date
+
+ if schedule_date in tuple(get_holidays(self.holiday_list)):
+ schedule_date = add_days(schedule_date, 1)
+ self.validate_workstation_holiday(schedule_date, skip_holiday_list_check=True)
+
+ return schedule_date
+
@frappe.whitelist()
def get_default_holiday_list():
return frappe.get_cached_value('Company', frappe.defaults.get_user_default("Company"), "default_holiday_list")
diff --git a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
index 9e0d1d1..7f0124b 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
@@ -6,8 +6,12 @@
'fieldname': 'workstation',
'transactions': [
{
- 'label': _('Manufacture'),
- 'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet']
+ 'label': _('Master'),
+ 'items': ['BOM', 'Routing', 'Operation']
+ },
+ {
+ 'label': _('Transaction'),
+ 'items': ['Work Order', 'Job Card', 'Timesheet']
}
]
}
diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
index be016ad..f7b407b 100644
--- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
+++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
+from frappe.utils.data import comma_and
def execute(filters=None):
# if not filters: filters = {}
@@ -13,35 +14,36 @@
data = get_bom_stock(filters)
qty_to_make = filters.get("qty_to_make")
+ manufacture_details = get_manufacturer_records()
for row in data:
- item_map = get_item_details(row.item_code)
reqd_qty = qty_to_make * row.actual_qty
last_pur_price = frappe.db.get_value("Item", row.item_code, "last_purchase_rate")
- if row.to_build > 0:
- diff_qty = row.to_build - reqd_qty
- summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, row.to_build, reqd_qty, diff_qty, last_pur_price])
- else:
- diff_qty = 0 - reqd_qty
- summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, "0.000", reqd_qty, diff_qty, last_pur_price])
+ summ_data.append(get_report_data(last_pur_price, reqd_qty, row, manufacture_details))
return columns, summ_data
+def get_report_data(last_pur_price, reqd_qty, row, manufacture_details):
+ to_build = row.to_build if row.to_build > 0 else 0
+ diff_qty = to_build - reqd_qty
+ return [row.item_code, row.description,
+ comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', []), add_quotes=False),
+ comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', []), add_quotes=False),
+ row.actual_qty, str(to_build),
+ reqd_qty, diff_qty, last_pur_price]
+
def get_columns():
"""return columns"""
columns = [
_("Item") + ":Link/Item:100",
_("Description") + "::150",
- _("Manufacturer") + "::100",
- _("Manufacturer Part Number") + "::100",
+ _("Manufacturer") + "::250",
+ _("Manufacturer Part Number") + "::250",
_("Qty") + ":Float:50",
_("Stock Qty") + ":Float:100",
_("Reqd Qty")+ ":Float:100",
_("Diff Qty")+ ":Float:100",
_("Last Purchase Price")+ ":Float:100",
-
-
]
-
return columns
def get_bom_stock(filters):
@@ -85,7 +87,12 @@
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
-def get_item_details(item_code):
- items = frappe.db.sql("""select it.item_group, it.item_name, it.stock_uom, it.name, it.brand, it.description, it.manufacturer_part_no, it.manufacturer from tabItem it where it.item_code = %s""", item_code, as_dict=1)
+def get_manufacturer_records():
+ details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"])
+ manufacture_details = frappe._dict()
+ for detail in details:
+ dic = manufacture_details.setdefault(detail.get('parent'), {})
+ dic.setdefault('manufacturer', []).append(detail.get('manufacturer'))
+ dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no'))
- return dict((d.name, d) for d in items)
+ return manufacture_details
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 07b646b..daedca7 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -646,4 +646,5 @@
erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields
erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
-erpnext.patches.v12_0.update_price_or_product_discount
\ No newline at end of file
+erpnext.patches.v12_0.update_price_or_product_discount
+erpnext.patches.v12_0.set_production_capacity_in_workstation
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
index f25b9ea..e47344b 100644
--- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
+++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
@@ -62,12 +62,12 @@
]
for dt in doctypes:
- for d in frappe.db.sql("""select name, parent, item_code, item_tax_rate from `tab{0} Item`
+ for d in frappe.db.sql("""select name, parenttype, parent, item_code, item_tax_rate from `tab{0} Item`
where ifnull(item_tax_rate, '') not in ('', '{{}}')
and item_tax_template is NULL""".format(dt), as_dict=1):
item_tax_map = json.loads(d.item_tax_rate)
item_tax_template_name = get_item_tax_template(item_tax_templates,
- item_tax_map, d.item_code, d.parent)
+ item_tax_map, d.item_code, d.parenttype, d.parent)
frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name)
frappe.db.auto_commit_on_many_writes = False
@@ -77,7 +77,7 @@
settings.determine_address_tax_category_from = "Billing Address"
settings.save()
-def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parent=None):
+def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None):
# search for previously created item tax template by comparing tax maps
for template, item_tax_template_map in iteritems(item_tax_templates):
if item_tax_map == item_tax_template_map:
@@ -88,23 +88,44 @@
item_tax_template.title = make_autoname("Item Tax Template-.####")
for tax_type, tax_rate in iteritems(item_tax_map):
- if not frappe.db.exists("Account", tax_type):
+ account_details = frappe.db.get_value("Account", tax_type, ['name', 'account_type'], as_dict=1)
+ if account_details:
+ if account_details.account_type not in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'):
+ frappe.db.set_value('Account', account_details.name, 'account_type', 'Chargeable')
+ else:
parts = tax_type.strip().split(" - ")
account_name = " - ".join(parts[:-1])
- company = frappe.db.get_value("Company", filters={"abbr": parts[-1]})
+ company = get_company(parts[-1], parenttype, parent)
parent_account = frappe.db.get_value("Account",
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account")
-
- frappe.get_doc({
- "doctype": "Account",
+ filters = {
"account_name": account_name,
- "company": company,
- "account_type": "Tax",
- "parent_account": parent_account
- }).insert()
+ "company": company,
+ "account_type": "Tax",
+ "parent_account": parent_account
+ }
+ tax_type = frappe.db.get_value("Account", filters)
+ if not tax_type:
+ account = frappe.new_doc("Account")
+ account.update(filters)
+ account.insert()
+ tax_type = account.name
item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
item_tax_templates.setdefault(item_tax_template.title, {})
item_tax_templates[item_tax_template.title][tax_type] = tax_rate
item_tax_template.save()
return item_tax_template.name
+
+def get_company(company_abbr, parenttype=None, parent=None):
+ if parenttype and parent:
+ company = frappe.get_cached_value(parenttype, parent, 'company')
+ else:
+ company = frappe.db.get_value("Company", filters={"abbr": company_abbr})
+
+ if not company:
+ companies = frappe.get_all('Company')
+ if len(companies) == 1:
+ company = companies[0].name
+
+ return company
diff --git a/erpnext/patches/v12_0/set_production_capacity_in_workstation.py b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py
new file mode 100644
index 0000000..bae1e28
--- /dev/null
+++ b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc("manufacturing", "doctype", "workstation")
+
+ frappe.db.sql(""" UPDATE `tabWorkstation`
+ SET production_capacity = 1 """)
\ No newline at end of file
diff --git a/erpnext/portal/doctype/homepage/test_homepage.py b/erpnext/portal/doctype/homepage/test_homepage.py
index b262c46..bf5c402 100644
--- a/erpnext/portal/doctype/homepage/test_homepage.py
+++ b/erpnext/portal/doctype/homepage/test_homepage.py
@@ -5,7 +5,7 @@
import frappe
import unittest
-from frappe.tests.test_website import set_request
+from frappe.utils import set_request
from frappe.website.render import render
class TestHomepage(unittest.TestCase):
diff --git a/erpnext/portal/doctype/homepage_section/test_homepage_section.py b/erpnext/portal/doctype/homepage_section/test_homepage_section.py
index c52b7a9..5b3196d 100644
--- a/erpnext/portal/doctype/homepage_section/test_homepage_section.py
+++ b/erpnext/portal/doctype/homepage_section/test_homepage_section.py
@@ -6,7 +6,7 @@
import frappe
import unittest
from bs4 import BeautifulSoup
-from frappe.tests.test_website import set_request
+from frappe.utils import set_request
from frappe.website.render import render
class TestHomepageSection(unittest.TestCase):
diff --git a/erpnext/portal/product_configurator/test_product_configurator.py b/erpnext/portal/product_configurator/test_product_configurator.py
index a534e5f..97042db 100644
--- a/erpnext/portal/product_configurator/test_product_configurator.py
+++ b/erpnext/portal/product_configurator/test_product_configurator.py
@@ -2,7 +2,7 @@
from bs4 import BeautifulSoup
import frappe, unittest
-from frappe.tests.test_website import set_request, get_html_for_route
+from frappe.utils import set_request, get_html_for_route
from frappe.website.render import render
from erpnext.portal.product_configurator.utils import get_products_for_website
from erpnext.stock.doctype.item.test_item import make_item_variant
diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py
index 61c50e5..3a373a4 100644
--- a/erpnext/portal/product_configurator/utils.py
+++ b/erpnext/portal/product_configurator/utils.py
@@ -313,13 +313,25 @@
search_condition = ''
if search:
+ # Default fields to search from
+ default_fields = {'name', 'item_name', 'description', 'item_group'}
+
+ # Get meta search fields
+ meta = frappe.get_meta("Item")
+ meta_fields = set(meta.get_search_fields())
+
+ # Join the meta fields and default fields set
+ search_fields = default_fields.union(meta_fields)
+ try:
+ if frappe.db.count('Item', cache=True) > 50000:
+ search_fields.remove('description')
+ except KeyError:
+ pass
+
+ # Build or filters for query
search = '%{}%'.format(search)
- or_filters = [
- ['name', 'like', search],
- ['item_name', 'like', search],
- ['description', 'like', search],
- ['item_group', 'like', search]
- ]
+ or_filters = [[field, 'like', search] for field in search_fields]
+
search_condition = get_conditions(or_filters, 'or')
filter_condition = get_conditions(filters, 'and')
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 25c97d1..3570a0f 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -4,20 +4,16 @@
setup(frm) {
frm.make_methods = {
'Timesheet': () => {
- let doctype = 'Timesheet';
- frappe.model.with_doctype(doctype, () => {
- let new_doc = frappe.model.get_new_doc(doctype);
-
- // add a new row and set the project
- let time_log = frappe.model.get_new_doc('Timesheet Detail');
- time_log.project = frm.doc.name;
- time_log.parent = new_doc.name;
- time_log.parentfield = 'time_logs';
- time_log.parenttype = 'Timesheet';
- new_doc.time_logs = [time_log];
-
- frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
- });
+ open_form(frm, "Timesheet", "Timesheet Detail", "time_logs");
+ },
+ 'Purchase Order': () => {
+ open_form(frm, "Purchase Order", "Purchase Order Item", "items");
+ },
+ 'Purchase Receipt': () => {
+ open_form(frm, "Purchase Receipt", "Purchase Receipt Item", "items");
+ },
+ 'Purchase Invoice': () => {
+ open_form(frm, "Purchase Invoice", "Purchase Invoice Item", "items");
},
};
},
@@ -80,7 +76,7 @@
frm.events.set_status(frm, 'Cancelled');
}, __('Set Status'));
}
-
+
if (frappe.model.can_read("Task")) {
frm.add_custom_button(__("Gantt Chart"), function () {
frappe.route_options = {
@@ -123,3 +119,20 @@
},
});
+
+function open_form(frm, doctype, child_doctype, parentfield) {
+ frappe.model.with_doctype(doctype, () => {
+ let new_doc = frappe.model.get_new_doc(doctype);
+
+ // add a new row and set the project
+ let new_child_doc = frappe.model.get_new_doc(child_doctype);
+ new_child_doc.project = frm.doc.name;
+ new_child_doc.parent = new_doc.name;
+ new_child_doc.parentfield = parentfield;
+ new_child_doc.parenttype = doctype;
+ new_doc[parentfield] = [new_child_doc];
+
+ frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
+ });
+
+}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index c4481c9..e908216 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -188,7 +188,8 @@
}, as_dict=True)
# check internal overlap
for time_log in self.time_logs:
- if not (time_log.from_time or time_log.to_time): continue
+ if not (time_log.from_time and time_log.to_time
+ and args.from_time and args.to_time): continue
if (fieldname != 'workstation' or args.get(fieldname) == time_log.get(fieldname)) and \
args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or
diff --git a/erpnext/public/images/illustrations/collaboration.png b/erpnext/public/images/illustrations/collaboration.png
deleted file mode 100644
index 12c67e3..0000000
--- a/erpnext/public/images/illustrations/collaboration.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/customer.png b/erpnext/public/images/illustrations/customer.png
deleted file mode 100644
index b2ddbf3..0000000
--- a/erpnext/public/images/illustrations/customer.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/customers-onboard.png b/erpnext/public/images/illustrations/customers-onboard.png
new file mode 100644
index 0000000..4a517bd
--- /dev/null
+++ b/erpnext/public/images/illustrations/customers-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/desk-onboard.png b/erpnext/public/images/illustrations/desk-onboard.png
new file mode 100644
index 0000000..74b632d
--- /dev/null
+++ b/erpnext/public/images/illustrations/desk-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/letterhead-onboard.png b/erpnext/public/images/illustrations/letterhead-onboard.png
new file mode 100644
index 0000000..fdfd16a
--- /dev/null
+++ b/erpnext/public/images/illustrations/letterhead-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/letterhead.png b/erpnext/public/images/illustrations/letterhead.png
deleted file mode 100644
index 37df6d7..0000000
--- a/erpnext/public/images/illustrations/letterhead.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/onboard.png b/erpnext/public/images/illustrations/onboard.png
deleted file mode 100644
index 094aa3f..0000000
--- a/erpnext/public/images/illustrations/onboard.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/product.png b/erpnext/public/images/illustrations/product.png
deleted file mode 100644
index f864b7a..0000000
--- a/erpnext/public/images/illustrations/product.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/products-onboard.png b/erpnext/public/images/illustrations/products-onboard.png
new file mode 100644
index 0000000..2dee203
--- /dev/null
+++ b/erpnext/public/images/illustrations/products-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/supplier-onboard.png b/erpnext/public/images/illustrations/supplier-onboard.png
new file mode 100644
index 0000000..30335f2
--- /dev/null
+++ b/erpnext/public/images/illustrations/supplier-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/supplier.png b/erpnext/public/images/illustrations/supplier.png
deleted file mode 100644
index 87f7789..0000000
--- a/erpnext/public/images/illustrations/supplier.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/user.png b/erpnext/public/images/illustrations/user.png
deleted file mode 100644
index 7dd7db2..0000000
--- a/erpnext/public/images/illustrations/user.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 5da9493..46a58fb 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1716,6 +1716,14 @@
}
},
+ against_blanket_order: function(doc, cdt, cdn) {
+ var item = locals[cdt][cdn];
+ if(!item.against_blanket_order) {
+ frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order", null);
+ frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order_rate", 0.00);
+ }
+ },
+
blanket_order: function(doc, cdt, cdn) {
var me = this;
var item = locals[cdt][cdn];
diff --git a/erpnext/regional/report/gstr_1/gstr_1.js b/erpnext/regional/report/gstr_1/gstr_1.js
index ce55921..1a7ff2b 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.js
+++ b/erpnext/regional/report/gstr_1/gstr_1.js
@@ -55,14 +55,25 @@
report.page.add_inner_button(__("Download as JSON"), function () {
var filters = report.get_values();
- const args = {
- cmd: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
- data: report.data,
- report_name: report.report_name,
- filters: filters
- };
-
- open_url_post(frappe.request.url, args);
+ frappe.call({
+ method: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
+ args: {
+ data: report.data,
+ report_name: report.report_name,
+ filters: filters
+ },
+ callback: function(r) {
+ if (r.message) {
+ const args = {
+ cmd: 'erpnext.regional.report.gstr_1.gstr_1.download_json_file',
+ data: r.message.data,
+ report_name: r.message.report_name,
+ report_type: r.message.report_type
+ };
+ open_url_post(frappe.request.url, args);
+ }
+ }
+ });
});
}
}
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 090616b..4f9cc7f 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -532,16 +532,9 @@
self.columns = self.invoice_columns + self.tax_columns + self.other_columns
@frappe.whitelist()
-def get_json():
- data = frappe._dict(frappe.local.form_dict)
-
- del data["cmd"]
- if "csrf_token" in data:
- del data["csrf_token"]
-
- filters = json.loads(data["filters"])
- report_data = json.loads(data["data"])
- report_name = data["report_name"]
+def get_json(filters, report_name, data):
+ filters = json.loads(filters)
+ report_data = json.loads(data)
gstin = get_company_gstin_number(filters["company"])
fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
@@ -575,7 +568,11 @@
out = get_export_json(res)
gst_json["exp"] = out
- download_json_file(report_name, filters["type_of_business"], gst_json)
+ return {
+ 'report_name': report_name,
+ 'report_type': filters['type_of_business'],
+ 'data': gst_json
+ }
def get_b2b_json(res, gstin):
inv_type, out = {"Registered Regular": "R", "Deemed Export": "DE", "URD": "URD", "SEZ": "SEZ"}, []
@@ -722,11 +719,15 @@
if gstin:
return gstin[0]["gstin"]
else:
- frappe.throw(_("Please set valid GSTIN No. in Company Address"))
+ frappe.throw(_("Please set valid GSTIN No. in Company Address for company {0}".format(
+ frappe.bold(company)
+ )))
-def download_json_file(filename, report_type, data):
+@frappe.whitelist()
+def download_json_file():
''' download json content in a file '''
- frappe.response['filename'] = frappe.scrub("{0} {1}".format(filename, report_type)) + '.json'
- frappe.response['filecontent'] = json.dumps(data)
+ data = frappe._dict(frappe.local.form_dict)
+ frappe.response['filename'] = frappe.scrub("{0} {1}".format(data['report_name'], data['report_type'])) + '.json'
+ frappe.response['filecontent'] = data['data']
frappe.response['content_type'] = 'application/json'
frappe.response['type'] = 'download'
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 85e8143..2dae0d8 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -112,7 +112,6 @@
let allow_delivery = false;
if (doc.docstatus==1) {
- this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
if(this.frm.has_perm("submit")) {
if(doc.status === 'On Hold') {
@@ -136,7 +135,7 @@
if(doc.status !== 'Closed') {
if(doc.status !== 'On Hold') {
- allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty))
+ allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty))
&& !this.frm.doc.skip_delivery_note
if (this.frm.has_perm("submit")) {
@@ -148,6 +147,8 @@
}
}
+ this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
+
// delivery note
if(flt(doc.per_delivered, 6) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
this.frm.add_custom_button(__('Delivery Note'), () => this.make_delivery_note_based_on_delivery_date(), __('Create'));
@@ -202,7 +203,7 @@
}
}
// payment request
- if(flt(doc.per_billed)==0) {
+ if(flt(doc.per_billed)<100) {
this.frm.add_custom_button(__('Payment Request'), () => this.make_payment_request(), __('Create'));
this.frm.add_custom_button(__('Payment'), () => this.make_payment_entry(), __('Create'));
}
@@ -361,7 +362,7 @@
},
toggle_delivery_date: function() {
- this.frm.fields_dict.items.grid.toggle_reqd("delivery_date",
+ this.frm.fields_dict.items.grid.toggle_reqd("delivery_date",
(this.frm.doc.order_type == "Sales" && !this.frm.doc.skip_delivery_note));
},
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index bd07841..feb6b76 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -12,6 +12,7 @@
from erpnext.controllers.accounts_controller import update_child_qty_rate
import json
from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request
+from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
class TestSalesOrder(unittest.TestCase):
def tearDown(self):
@@ -819,6 +820,25 @@
mr_doc = frappe.get_doc('Material Request',mr.get('name'))
self.assertEqual(mr_doc.items[0].sales_order, so.name)
+ def test_so_optional_blanket_order(self):
+ """
+ Expected result: Blanket order Ordered Quantity should only be affected on Sales Order with against_blanket_order = 1.
+ Second Sales Order should not add on to Blanket Orders Ordered Quantity.
+ """
+
+ bo = make_blanket_order(blanket_order_type = "Selling", quantity = 10, rate = 10)
+
+ so = make_sales_order(item_code= "_Test Item", qty = 5, against_blanket_order = 1)
+ so_doc = frappe.get_doc('Sales Order', so.get('name'))
+ # To test if the SO has a Blanket Order
+ self.assertTrue(so_doc.items[0].blanket_order)
+
+ so = make_sales_order(item_code= "_Test Item", qty = 5, against_blanket_order = 0)
+ so_doc = frappe.get_doc('Sales Order', so.get('name'))
+ # To test if the SO does NOT have a Blanket Order
+ self.assertEqual(so_doc.items[0].blanket_order, None)
+
+
def make_sales_order(**args):
so = frappe.new_doc("Sales Order")
args = frappe._dict(args)
@@ -845,7 +865,8 @@
"warehouse": args.warehouse,
"qty": args.qty or 10,
"uom": args.uom or None,
- "rate": args.rate or 100
+ "rate": args.rate or 100,
+ "against_blanket_order": args.against_blanket_order
})
so.delivery_date = add_days(so.transaction_date, 10)
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 3fd1e64..86b09c2 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -68,6 +68,7 @@
"target_warehouse",
"prevdoc_docname",
"col_break4",
+ "against_blanket_order",
"blanket_order",
"blanket_order_rate",
"planning_section",
@@ -574,6 +575,7 @@
"report_hide": 1
},
{
+ "depends_on": "eval:doc.against_blanket_order",
"fieldname": "blanket_order",
"fieldtype": "Link",
"label": "Blanket Order",
@@ -581,6 +583,7 @@
"options": "Blanket Order"
},
{
+ "depends_on": "eval:doc.against_blanket_order",
"fieldname": "blanket_order_rate",
"fieldtype": "Currency",
"label": "Blanket Order Rate",
@@ -741,11 +744,17 @@
"fieldname": "image_section",
"fieldtype": "Section Break",
"label": "Image"
+ },
+ {
+ "default": "0",
+ "fieldname": "against_blanket_order",
+ "fieldtype": "Check",
+ "label": "Against Blanket Order"
}
],
"idx": 1,
"istable": 1,
- "modified": "2019-10-10 08:46:26.244823",
+ "modified": "2019-11-19 14:19:29.491945",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index dc2c4ce..5033d7a 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -1,611 +1,159 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-06-25 10:25:16",
- "custom": 0,
- "description": "Settings for Selling Module",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Other",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "creation": "2013-06-25 10:25:16",
+ "description": "Settings for Selling Module",
+ "doctype": "DocType",
+ "document_type": "Other",
+ "engine": "InnoDB",
+ "field_order": [
+ "cust_master_name",
+ "campaign_naming_by",
+ "customer_group",
+ "territory",
+ "selling_price_list",
+ "close_opportunity_after_days",
+ "default_valid_till",
+ "column_break_5",
+ "so_required",
+ "dn_required",
+ "sales_update_frequency",
+ "maintain_same_sales_rate",
+ "editable_price_list_rate",
+ "allow_multiple_items",
+ "allow_against_multiple_purchase_orders",
+ "validate_selling_price",
+ "hide_tax_id"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Customer Name",
- "fieldname": "cust_master_name",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Customer Naming By",
- "length": 0,
- "no_copy": 0,
- "options": "Customer Name\nNaming Series",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "Customer Name",
+ "fieldname": "cust_master_name",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Customer Naming By",
+ "options": "Customer Name\nNaming Series"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "campaign_naming_by",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Campaign Naming By",
- "length": 0,
- "no_copy": 0,
- "options": "Campaign Name\nNaming Series",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "campaign_naming_by",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Campaign Naming By",
+ "options": "Campaign Name\nNaming Series"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "customer_group",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Default Customer Group",
- "length": 0,
- "no_copy": 0,
- "options": "Customer Group",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "customer_group",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Default Customer Group",
+ "options": "Customer Group"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "territory",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Default Territory",
- "length": 0,
- "no_copy": 0,
- "options": "Territory",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "territory",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Default Territory",
+ "options": "Territory"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "selling_price_list",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Default Price List",
- "length": 0,
- "no_copy": 0,
- "options": "Price List",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "selling_price_list",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Default Price List",
+ "options": "Price List"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "15",
- "description": "Auto close Opportunity after 15 days",
- "fieldname": "close_opportunity_after_days",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Close Opportunity After Days",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "15",
+ "description": "Auto close Opportunity after 15 days",
+ "fieldname": "close_opportunity_after_days",
+ "fieldtype": "Int",
+ "label": "Close Opportunity After Days"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "default_valid_till",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Default Quotation Validity Days",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "default_valid_till",
+ "fieldtype": "Data",
+ "label": "Default Quotation Validity Days"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_5",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "so_required",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Sales Order Required",
- "length": 0,
- "no_copy": 0,
- "options": "No\nYes",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "description": "Only for Stock Items",
+ "fieldname": "so_required",
+ "fieldtype": "Select",
+ "label": "Sales Order Required",
+ "options": "No\nYes"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "dn_required",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Delivery Note Required",
- "length": 0,
- "no_copy": 0,
- "options": "No\nYes",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "dn_required",
+ "fieldtype": "Select",
+ "label": "Delivery Note Required",
+ "options": "No\nYes"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Each Transaction",
- "description": "How often should project and company be updated based on Sales Transactions.",
- "fieldname": "sales_update_frequency",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Sales Update Frequency",
- "length": 0,
- "no_copy": 0,
- "options": "Each Transaction\nDaily\nMonthly",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "Each Transaction",
+ "description": "How often should project and company be updated based on Sales Transactions.",
+ "fieldname": "sales_update_frequency",
+ "fieldtype": "Select",
+ "label": "Sales Update Frequency",
+ "options": "Each Transaction\nDaily\nMonthly",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "maintain_same_sales_rate",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Maintain Same Rate Throughout Sales Cycle",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "maintain_same_sales_rate",
+ "fieldtype": "Check",
+ "label": "Maintain Same Rate Throughout Sales Cycle"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "editable_price_list_rate",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Allow user to edit Price List Rate in transactions",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "editable_price_list_rate",
+ "fieldtype": "Check",
+ "label": "Allow user to edit Price List Rate in transactions"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "allow_multiple_items",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Allow Item to be added multiple times in a transaction",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "allow_multiple_items",
+ "fieldtype": "Check",
+ "label": "Allow Item to be added multiple times in a transaction"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "allow_against_multiple_purchase_orders",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Allow multiple Sales Orders against a Customer's Purchase Order",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "allow_against_multiple_purchase_orders",
+ "fieldtype": "Check",
+ "label": "Allow multiple Sales Orders against a Customer's Purchase Order"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "validate_selling_price",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Validate Selling Price for Item against Purchase Rate or Valuation Rate",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "validate_selling_price",
+ "fieldtype": "Check",
+ "label": "Validate Selling Price for Item against Purchase Rate or Valuation Rate"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "hide_tax_id",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Hide Customer's Tax Id from Sales Transactions",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "default": "0",
+ "fieldname": "hide_tax_id",
+ "fieldtype": "Check",
+ "label": "Hide Customer's Tax Id from Sales Transactions"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "fa fa-cog",
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-06-25 12:56:16.332039",
- "modified_by": "Administrator",
- "module": "Selling",
- "name": "Selling Settings",
- "owner": "Administrator",
+ ],
+ "icon": "fa fa-cog",
+ "idx": 1,
+ "issingle": 1,
+ "modified": "2019-11-25 18:35:51.472653",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Selling Settings",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 0,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 0,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
-}
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/selling_settings/test_selling_settings.py b/erpnext/selling/doctype/selling_settings/test_selling_settings.py
new file mode 100644
index 0000000..961a54d
--- /dev/null
+++ b/erpnext/selling/doctype/selling_settings/test_selling_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestSellingSettings(unittest.TestCase):
+ pass
diff --git a/erpnext/selling/setup_wizard_slide/add_a_few_customers/add_a_few_customers.json b/erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json
similarity index 77%
rename from erpnext/selling/setup_wizard_slide/add_a_few_customers/add_a_few_customers.json
rename to erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json
index a0bb6fe..f39fea4 100644
--- a/erpnext/selling/setup_wizard_slide/add_a_few_customers/add_a_few_customers.json
+++ b/erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json
@@ -3,18 +3,18 @@
"app": "ERPNext",
"creation": "2019-11-15 14:44:10.065014",
"docstatus": 0,
- "doctype": "Setup Wizard Slide",
+ "doctype": "Onboarding Slide",
"domains": [],
"help_links": [
{
- "label": "Customers",
+ "label": "Learn More",
"video_id": "zsrrVDk6VBs"
}
],
"idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/customer.png",
+ "image_src": "/assets/erpnext/images/illustrations/customers-onboard.png",
"max_count": 3,
- "modified": "2019-11-26 18:26:15.888794",
+ "modified": "2019-12-03 22:54:28.959549",
"modified_by": "Administrator",
"name": "Add A Few Customers",
"owner": "Administrator",
@@ -44,6 +44,5 @@
],
"slide_order": 40,
"slide_title": "Add A Few Customers",
- "slide_type": "Create",
- "submit_method": ""
+ "slide_type": "Create"
}
\ No newline at end of file
diff --git a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py
index 32711b2..056492a 100644
--- a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py
+++ b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py
@@ -7,7 +7,7 @@
def execute(filters=None):
if not filters: filters = {}
-
+
columns = get_columns()
iwq_map = get_item_warehouse_quantity_map()
item_map = get_item_details()
@@ -15,22 +15,23 @@
for sbom, warehouse in iwq_map.items():
total = 0
total_qty = 0
-
+
for wh, item_qty in warehouse.items():
total += 1
- row = [sbom, item_map.get(sbom).item_name, item_map.get(sbom).description,
- item_map.get(sbom).stock_uom, wh]
- available_qty = item_qty
- total_qty += flt(available_qty)
- row += [available_qty]
-
- if available_qty:
- data.append(row)
- if (total == len(warehouse)):
- row = ["", "", "Total", "", "", total_qty]
+ if item_map.get(sbom):
+ row = [sbom, item_map.get(sbom).item_name, item_map.get(sbom).description,
+ item_map.get(sbom).stock_uom, wh]
+ available_qty = item_qty
+ total_qty += flt(available_qty)
+ row += [available_qty]
+
+ if available_qty:
data.append(row)
+ if (total == len(warehouse)):
+ row = ["", "", "Total", "", "", total_qty]
+ data.append(row)
return columns, data
-
+
def get_columns():
columns = ["Item Code:Link/Item:100", "Item Name::100", "Description::120", \
"UOM:Link/UOM:80", "Warehouse:Link/Warehouse:100", "Quantity::100"]
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index 0bcddc2..4d2d540 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -283,7 +283,7 @@
card.value = card.value *-1
card.value = self.fmt_money(card.value,False if key in ("bank_balance", "credit_balance") else True)
- cache.setex(cache_key, card, 24 * 60 * 60)
+ cache.set_value(cache_key, card, expires_in_sec=24 * 60 * 60)
context.cards.append(card)
diff --git "a/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json" "b/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json"
new file mode 100644
index 0000000..bf330d0
--- /dev/null
+++ "b/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json"
@@ -0,0 +1,23 @@
+{
+ "add_more_button": 0,
+ "app": "ERPNext",
+ "creation": "2019-12-04 19:21:39.995776",
+ "docstatus": 0,
+ "doctype": "Onboarding Slide",
+ "domains": [],
+ "help_links": [],
+ "idx": 0,
+ "image_src": "/assets/erpnext/images/illustrations/desk-onboard.png",
+ "is_completed": 0,
+ "max_count": 3,
+ "modified": "2019-12-04 19:21:39.995776",
+ "modified_by": "Administrator",
+ "name": "Welcome back to ERPNext!",
+ "owner": "Administrator",
+ "slide_desc": "<p>Let's continue where you left from!</p>",
+ "slide_fields": [],
+ "slide_module": "Setup",
+ "slide_order": 0,
+ "slide_title": "Welcome back to ERPNext!",
+ "slide_type": "Continue"
+}
\ No newline at end of file
diff --git "a/erpnext/setup/setup_wizard_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json" "b/erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
similarity index 60%
rename from "erpnext/setup/setup_wizard_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
rename to "erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
index 1da9dd4..4ea6985 100644
--- "a/erpnext/setup/setup_wizard_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
+++ "b/erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
@@ -3,20 +3,20 @@
"app": "ERPNext",
"creation": "2019-11-26 17:01:26.671859",
"docstatus": 0,
- "doctype": "Setup Wizard Slide",
+ "doctype": "Onboarding Slide",
"domains": [],
"help_links": [],
"idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/onboard.png",
+ "image_src": "/assets/erpnext/images/illustrations/desk-onboard.png",
"max_count": 0,
- "modified": "2019-11-26 17:17:29.813299",
+ "modified": "2019-12-03 22:49:12.871260",
"modified_by": "Administrator",
"name": "Welcome to ERPNext!",
"owner": "Administrator",
- "slide_desc": "Setting up an ERP can be overwhelming. But don't worry, we have got your back!<br>\nLet's setup your company.\nThis wizard will help you onboard to ERPNext in a short time!",
+ "slide_desc": "Setting up an ERP can be overwhelming. But don't worry, we have got your back!\nLet's setup your company.\nThis wizard will help you onboard to ERPNext in a short time!",
"slide_fields": [],
"slide_module": "Setup",
- "slide_order": 10,
+ "slide_order": 1,
"slide_title": "Welcome to ERPNext!",
"slide_type": "Information"
}
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/test_setup_wizard.py b/erpnext/setup/setup_wizard/test_setup_wizard.py
deleted file mode 100644
index a489133..0000000
--- a/erpnext/setup/setup_wizard/test_setup_wizard.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-
-import frappe, time
-from frappe.utils.selenium_testdriver import TestDriver
-
-def run_setup_wizard_test():
- driver = TestDriver()
- frappe.db.set_default('in_selenium', '1')
- frappe.db.commit()
-
- driver.login('#page-setup-wizard')
- print('Running Setup Wizard Test...')
-
- # Language slide
- driver.wait_for_ajax(True)
- time.sleep(1)
-
- driver.set_select("language", "English (United States)")
- driver.wait_for_ajax(True)
- time.sleep(1)
- driver.click(".next-btn")
-
- # Region slide
- driver.wait_for_ajax(True)
- driver.set_select("country", "India")
- driver.wait_for_ajax(True)
- time.sleep(1)
- driver.click(".next-btn")
-
- # Profile slide
- driver.set_field("full_name", "Great Tester")
- driver.set_field("email", "great@example.com")
- driver.set_field("password", "test")
- driver.wait_for_ajax(True)
- time.sleep(1)
- driver.click(".next-btn")
- time.sleep(1)
-
- # domain slide
- driver.set_multicheck("domains", ["Manufacturing"])
- time.sleep(1)
- driver.click(".next-btn")
-
- # Org slide
- driver.set_field("company_name", "For Testing")
- time.sleep(1)
- driver.print_console()
- driver.click(".next-btn")
-
- driver.set_field("company_tagline", "Just for GST")
- driver.set_field("bank_account", "HDFC")
- time.sleep(3)
- driver.click(".complete-btn")
-
- # Wait for desktop
- driver.wait_for('#page-desktop', timeout=600)
-
- driver.print_console()
- time.sleep(3)
-
- frappe.db.set_default('in_selenium', None)
- frappe.db.set_value("Company", "For Testing", "write_off_account", "Write Off - FT")
- frappe.db.set_value("Company", "For Testing", "exchange_gain_loss_account", "Exchange Gain/Loss - FT")
- frappe.db.commit()
-
- driver.close()
-
- return True
diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py
index d1c206d..1a86b79 100644
--- a/erpnext/setup/utils.py
+++ b/erpnext/setup/utils.py
@@ -106,7 +106,8 @@
# expire in 6 hours
response.raise_for_status()
value = response.json()["rates"][to_currency]
- cache.setex(key, value, 6 * 60 * 60)
+
+ cache.set_value(key, value, expires_in_sec=6 * 60 * 60)
return flt(value)
except:
frappe.log_error(title="Get Exchange Rate")
diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py
index 831381c..5341f29 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -65,7 +65,7 @@
bin = get_bin_qty(packing_item_code, pi.warehouse)
pi.actual_qty = flt(bin.get("actual_qty"))
pi.projected_qty = flt(bin.get("projected_qty"))
- if old_packed_items_map:
+ if old_packed_items_map and old_packed_items_map.get((packing_item_code, main_item_row.item_code)):
pi.batch_no = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].batch_no
pi.serial_no = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].serial_no
pi.warehouse = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].warehouse
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index d9c94fc..4770471 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -249,6 +249,8 @@
}, __("Get items from"));
}
+ frm.events.show_bom_custom_button(frm);
+
if (frm.doc.company) {
frm.trigger("toggle_display_account_head");
}
@@ -262,6 +264,11 @@
frm.trigger("setup_quality_inspection");
},
+ stock_entry_type: function(frm){
+ frm.remove_custom_button('Bill of Materials', "Get items from");
+ frm.events.show_bom_custom_button(frm);
+ },
+
purpose: function(frm) {
frm.trigger('validate_purpose_consumption');
frm.fields_dict.items.grid.refresh();
@@ -398,6 +405,85 @@
}
},
+ show_bom_custom_button: function(frm){
+ if (frm.doc.docstatus === 0 &&
+ ['Material Issue', 'Material Receipt', 'Material Transfer', 'Send to Subcontractor'].includes(frm.doc.purpose)) {
+ frm.add_custom_button(__('Bill of Materials'), function() {
+ frm.events.get_items_from_bom(frm);
+ }, __("Get items from"));
+ }
+ },
+
+ get_items_from_bom: function(frm) {
+ let filters = function(){
+ return {filters: { docstatus:1 }};
+ }
+
+ let fields = [
+ {"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"),
+ options:"BOM", reqd: 1, get_query: filters()},
+ {"fieldname":"source_warehouse", "fieldtype":"Link", "label":__("Source Warehouse"),
+ options:"Warehouse"},
+ {"fieldname":"target_warehouse", "fieldtype":"Link", "label":__("Target Warehouse"),
+ options:"Warehouse"},
+ {"fieldname":"qty", "fieldtype":"Float", "label":__("Quantity"),
+ reqd: 1, "default": 1},
+ {"fieldname":"fetch_exploded", "fieldtype":"Check",
+ "label":__("Fetch exploded BOM (including sub-assemblies)"), "default":1},
+ {"fieldname":"fetch", "label":__("Get Items from BOM"), "fieldtype":"Button"}
+ ]
+
+ // Exclude field 'Target Warehouse' in case of Material Issue
+ if (frm.doc.purpose == 'Material Issue'){
+ fields.splice(2,1);
+ }
+ // Exclude field 'Source Warehouse' in case of Material Receipt
+ else if(frm.doc.purpose == 'Material Receipt'){
+ fields.splice(1,1);
+ }
+
+ let d = new frappe.ui.Dialog({
+ title: __("Get Items from BOM"),
+ fields: fields
+ });
+ d.get_input("fetch").on("click", function() {
+ let values = d.get_values();
+ if(!values) return;
+ values["company"] = frm.doc.company;
+ if(!frm.doc.company) frappe.throw(__("Company field is required"));
+ frappe.call({
+ method: "erpnext.manufacturing.doctype.bom.bom.get_bom_items",
+ args: values,
+ callback: function(r) {
+ if (!r.message) {
+ frappe.throw(__("BOM does not contain any stock item"));
+ } else {
+ erpnext.utils.remove_empty_first_row(frm, "items");
+ $.each(r.message, function(i, item) {
+ let d = frappe.model.add_child(cur_frm.doc, "Stock Entry Detail", "items");
+ d.item_code = item.item_code;
+ d.item_name = item.item_name;
+ d.item_group = item.item_group;
+ d.s_warehouse = values.source_warehouse;
+ d.t_warehouse = values.target_warehouse;
+ d.uom = item.stock_uom;
+ d.stock_uom = item.stock_uom;
+ d.conversion_factor = item.conversion_factor ? item.conversion_factor : 1;
+ d.qty = item.qty;
+ d.expense_account = item.expense_account;
+ d.project = item.project;
+ frm.events.set_basic_rate(frm, d.doctype, d.name);
+ });
+ }
+ d.hide();
+ refresh_field("items");
+ }
+ });
+
+ });
+ d.show();
+ },
+
calculate_basic_amount: function(frm, item) {
item.basic_amount = flt(flt(item.transfer_qty) * flt(item.basic_rate),
precision("basic_amount", item));
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 55f4be1..76644ed 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -213,7 +213,8 @@
project: "",
qty: "",
stock_qty: "",
- conversion_factor: ""
+ conversion_factor: "",
+ against_blanket_order: 0/1
}
:param item: `item_code` of Item object
:return: frappe._dict
@@ -302,7 +303,8 @@
"weight_per_unit":item.weight_per_unit,
"weight_uom":item.weight_uom,
"last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0,
- "transaction_date": args.get("transaction_date")
+ "transaction_date": args.get("transaction_date"),
+ "against_blanket_order": args.get("against_blanket_order")
})
if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
@@ -996,9 +998,10 @@
def update_party_blanket_order(args, out):
- blanket_order_details = get_blanket_order_details(args)
- if blanket_order_details:
- out.update(blanket_order_details)
+ if out["against_blanket_order"]:
+ blanket_order_details = get_blanket_order_details(args)
+ if blanket_order_details:
+ out.update(blanket_order_details)
@frappe.whitelist()
def get_blanket_order_details(args):
diff --git a/erpnext/stock/setup_wizard_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json b/erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
similarity index 76%
rename from erpnext/stock/setup_wizard_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
rename to erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
index c536f7b..27a3062 100644
--- a/erpnext/stock/setup_wizard_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
+++ b/erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
@@ -3,13 +3,13 @@
"app": "ERPNext",
"creation": "2019-11-15 14:41:12.007359",
"docstatus": 0,
- "doctype": "Setup Wizard Slide",
+ "doctype": "Onboarding Slide",
"domains": [],
"help_links": [],
"idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/product.png",
+ "image_src": "/assets/erpnext/images/illustrations/products-onboard.png",
"max_count": 3,
- "modified": "2019-11-26 18:26:35.305755",
+ "modified": "2019-12-03 22:54:07.558632",
"modified_by": "Administrator",
"name": "Add A Few Products You Buy Or Sell",
"owner": "Administrator",
@@ -26,15 +26,9 @@
},
{
"align": "",
- "fieldtype": "Column Break",
- "reqd": 1
- },
- {
- "align": "",
- "fieldname": "uom",
- "fieldtype": "Link",
- "label": "UOM",
- "options": "UOM",
+ "fieldname": "item_price",
+ "fieldtype": "Currency",
+ "label": "Item Price",
"reqd": 1
},
{
@@ -44,14 +38,14 @@
},
{
"align": "",
- "fieldname": "item_price",
- "fieldtype": "Currency",
- "label": "Item Price",
+ "fieldname": "uom",
+ "fieldtype": "Link",
+ "label": "UOM",
+ "options": "UOM",
"reqd": 1
}
],
"slide_order": 30,
"slide_title": "Add A Few Products You Buy Or Sell",
- "slide_type": "Create",
- "submit_method": ""
+ "slide_type": "Create"
}
\ No newline at end of file
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index 2e25a12..cdff3ff 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -1329,7 +1329,7 @@
apps/erpnext/erpnext/utilities/transaction_base.py,Invalid Posting Time,Ungültige Buchungszeit
DocType: Salary Component,Condition and Formula,Zustand und Formel
DocType: Lead,Campaign Name,Kampagnenname
-apps/erpnext/erpnext/setup/default_energy_point_rules.py,On Task Completion,Bei Abschluss der Aufgabe
+apps/erpnext/erpnext/setup/default_energy_point_rules.py,On Task Completion,Bei Abschluss des Vorgangs
apps/erpnext/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py,There is no leave period in between {0} and {1},Es gibt keinen Urlaub zwischen {0} und {1}
DocType: Fee Validity,Healthcare Practitioner,praktischer Arzt
DocType: Hotel Room,Capacity,Kapazität
@@ -1353,7 +1353,7 @@
apps/erpnext/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py,Payment Cancelled. Please check your GoCardless Account for more details,Zahlung abgebrochen. Bitte überprüfen Sie Ihr GoCardless Konto für weitere Details
DocType: Work Order,Skip Material Transfer to WIP Warehouse,Überspringen Sie die Materialübertragung in das WIP-Lager
DocType: Contract,N/A,nicht verfügbar
-DocType: Task Type,Task Type,Aufgabentyp
+DocType: Task Type,Task Type,Vorgangstyp
DocType: Topic,Topic Content,Themeninhalt
DocType: Delivery Settings,Send with Attachment,Senden mit Anhang
DocType: Service Level,Priorities,Prioritäten
@@ -2449,7 +2449,7 @@
DocType: Asset,Depreciation Schedules,Abschreibungen Termine
apps/erpnext/erpnext/projects/doctype/timesheet/timesheet.js,Create Sales Invoice,Verkaufsrechnung erstellen
apps/erpnext/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html,Ineligible ITC,Nicht förderfähiges ITC
-DocType: Task,Dependent Tasks,Abhängige Aufgaben
+DocType: Task,Dependent Tasks,Abhängige Vorgänge
apps/erpnext/erpnext/regional/report/gstr_1/gstr_1.py,Following accounts might be selected in GST Settings:,In den GST-Einstellungen können folgende Konten ausgewählt werden:
apps/erpnext/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js,Quantity to Produce,Menge zu produzieren
apps/erpnext/erpnext/hr/doctype/leave_application/leave_application.py,Application period cannot be outside leave allocation period,Beantragter Zeitraum kann nicht außerhalb der beantragten Urlaubszeit liegen
@@ -2846,7 +2846,7 @@
DocType: Purchase Invoice,03-Deficiency in services,03-Mangel an Dienstleistungen
DocType: Healthcare Settings,Default Medical Code Standard,Default Medical Code Standard
DocType: Purchase Invoice Item,HSN/SAC,HSN / SAC
-DocType: Project Template Task,Project Template Task,Projektvorlagenaufgabe
+DocType: Project Template Task,Project Template Task,Projektvorgangsvorlage
DocType: Accounts Settings,Over Billing Allowance (%),Mehr als Abrechnungsbetrag (%)
apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py,Purchase Receipt {0} is not submitted,Kaufbeleg {0} wurde nicht übertragen
DocType: Company,Default Payable Account,Standard-Verbindlichkeitenkonto
@@ -3323,7 +3323,7 @@
,Qty to Order,Zu bestellende Menge
DocType: Period Closing Voucher,"The account head under Liability or Equity, in which Profit/Loss will be booked","Der Kontenkopf unter Eigen- oder Fremdkapital, in dem Gewinn / Verlust verbucht wird"
apps/erpnext/erpnext/accounts/doctype/budget/budget.py,Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4},Ein weiterer Budgeteintrag '{0}' existiert bereits für {1} '{2}' und für '{3}' für das Geschäftsjahr {4}
-apps/erpnext/erpnext/config/projects.py,Gantt chart of all tasks.,Gantt-Diagramm aller Aufgaben
+apps/erpnext/erpnext/config/projects.py,Gantt chart of all tasks.,Gantt-Diagramm aller Vorgänge
DocType: Opportunity,Mins to First Response,Minuten zum First Response
DocType: Pricing Rule,Margin Type,Margenart
apps/erpnext/erpnext/projects/doctype/project/project_dashboard.html,{0} hours,{0} Stunden
@@ -3961,7 +3961,7 @@
apps/erpnext/erpnext/education/doctype/student/student_dashboard.py,Student LMS Activity,Student LMS Aktivität
DocType: POS Profile,Applicable for Users,Anwendbar für Benutzer
DocType: Supplier Quotation,PUR-SQTN-.YYYY.-,PUR-SQTN-.JJJJ.-
-apps/erpnext/erpnext/projects/doctype/project/project.js,Set Project and all Tasks to status {0}?,Projekt und alle Aufgaben auf Status {0} setzen?
+apps/erpnext/erpnext/projects/doctype/project/project.js,Set Project and all Tasks to status {0}?,Projekt und alle Vorgänge auf Status {0} setzen?
DocType: Purchase Invoice,Set Advances and Allocate (FIFO),Vorschüsse setzen und zuordnen (FIFO)
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.py,No Work Orders created,Keine Arbeitsaufträge erstellt
apps/erpnext/erpnext/hr/doctype/salary_slip/salary_slip.py,Salary Slip of employee {0} already created for this period,Gehaltsabrechnung der Mitarbeiter {0} für diesen Zeitraum bereits erstellt
@@ -4418,7 +4418,7 @@
DocType: Hotel Room,Hotels,Hotels
apps/erpnext/erpnext/accounts/doctype/cost_center/cost_center_tree.js,New Cost Center Name,Neuer Kostenstellenname
DocType: Leave Control Panel,Leave Control Panel,Urlaubsverwaltung
-DocType: Project,Task Completion,Aufgabenerledigung
+DocType: Project,Task Completion,Vorgangserfüllung
apps/erpnext/erpnext/templates/generators/item/item_add_to_cart.html,Not in Stock,Nicht lagernd
DocType: Volunteer,Volunteer Skills,Freiwillige Fähigkeiten
DocType: Additional Salary,HR User,Nutzer Personalabteilung
@@ -5197,7 +5197,7 @@
apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py,Account {0} does not exists,Konto {0} existiert nicht
apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.js,Select Loyalty Program,Wählen Sie Treueprogramm
DocType: Project,Project Type,Projekttyp
-apps/erpnext/erpnext/projects/doctype/task/task.py,Child Task exists for this Task. You can not delete this Task.,Für diese Aufgabe existiert eine untergeordnete Aufgabe. Sie können diese Aufgabe daher nicht löschen.
+apps/erpnext/erpnext/projects/doctype/task/task.py,Child Task exists for this Task. You can not delete this Task.,Für diesen Vorgang existiert ein untergeordneter Vorgang. Sie können diese Aufgabe daher nicht löschen.
apps/erpnext/erpnext/setup/doctype/sales_person/sales_person.py,Either target qty or target amount is mandatory.,Entweder Zielstückzahl oder Zielmenge ist zwingend erforderlich.
apps/erpnext/erpnext/config/projects.py,Cost of various activities,Aufwendungen für verschiedene Tätigkeiten
apps/erpnext/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py,"Setting Events to {0}, since the Employee attached to the below Sales Persons does not have a User ID{1}","Einstellen Events auf {0}, da die Mitarbeiter auf die beigefügten unter Verkaufs Personen keine Benutzer-ID {1}"
@@ -5597,7 +5597,7 @@
apps/erpnext/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py,{0} is not a valid Batch Number for Item {1},{0} ist keine gültige Chargennummer für Artikel {1}
apps/erpnext/erpnext/shopping_cart/cart.py,Please enter valid coupon code !!,Bitte geben Sie einen gültigen Gutscheincode ein !!
apps/erpnext/erpnext/hr/doctype/leave_application/leave_application.py,Note: There is not enough leave balance for Leave Type {0},Hinweis: Es gibt nicht genügend Urlaubsguthaben für Abwesenheitstyp {0}
-DocType: Task,Task Description,Aufgabenbeschreibung
+DocType: Task,Task Description,Vorgangsbeschreibung
DocType: Training Event,Seminar,Seminar
DocType: Program Enrollment Fee,Program Enrollment Fee,Programm Einschreibegebühr
DocType: Item,Supplier Items,Lieferantenartikel
@@ -5754,7 +5754,7 @@
DocType: Lost Reason Detail,Lost Reason Detail,Verlorene Begründung Detail
apps/erpnext/erpnext/hr/utils.py,Please set leave policy for employee {0} in Employee / Grade record,Legen Sie die Abwesenheitsrichtlinie für den Mitarbeiter {0} im Mitarbeiter- / Notensatz fest
apps/erpnext/erpnext/public/js/controllers/transaction.js,Invalid Blanket Order for the selected Customer and Item,Ungültiger Blankoauftrag für den ausgewählten Kunden und Artikel
-apps/erpnext/erpnext/projects/doctype/task/task_tree.js,Add Multiple Tasks,Mehrere Aufgaben hinzufügen
+apps/erpnext/erpnext/projects/doctype/task/task_tree.js,Add Multiple Tasks,Mehrere Vorgänge hinzufügen
DocType: Purchase Invoice,Items,Artikel
apps/erpnext/erpnext/crm/doctype/contract/contract.py,End Date cannot be before Start Date.,Das Enddatum darf nicht vor dem Startdatum liegen.
apps/erpnext/erpnext/education/doctype/course_enrollment/course_enrollment.py,Student is already enrolled.,Student ist bereits eingetragen sind.