Merge branch 'version-12-hotfix' into version-12

This commit is contained in:
Sahil Khan 2019-08-07 16:21:14 +05:30
commit 9335cdd536
25 changed files with 1405 additions and 2555 deletions

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '12.0.4'
__version__ = '12.0.5'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -6,16 +6,10 @@ from frappe import _
def get_data():
return {
'fieldname': 'bank',
'non_standard_fieldnames': {
'Paymnet Order': 'company_bank'
},
'transactions': [
{
'label': _('Bank Deatils'),
'items': ['Bank Account', 'Bank Guarantee']
},
{
'items': ['Payment Order']
}
]
}

View File

@ -648,13 +648,18 @@ def get_orders_to_be_billed(posting_date, party_type, party,
orders = []
if voucher_type:
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"
else:
grand_total_field = "grand_total"
rounded_total_field = "rounded_total"
orders = frappe.db.sql("""
select
name as voucher_no,
{ref_field} as invoice_amount,
({ref_field} - advance_paid) as outstanding_amount,
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
transaction_date as posting_date
from
`tab{voucher_type}`
@ -663,13 +668,14 @@ def get_orders_to_be_billed(posting_date, party_type, party,
and docstatus = 1
and company = %s
and ifnull(status, "") != "Closed"
and {ref_field} > advance_paid
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
and abs(100 - per_billed) > 0.01
{condition}
order by
transaction_date, name
""".format(**{
"ref_field": ref_field,
"rounded_total_field": rounded_total_field,
"grand_total_field": grand_total_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"condition": condition

View File

@ -382,7 +382,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
`tab{child_doc}`.amount
FROM `tab{child_doc}`, `tab{parent_doc}`
WHERE
`tab{child_doc}`.parent = `tab{parent_doc}`.name and {date_field}
`tab{child_doc}`.parent = `tab{parent_doc}`.name and `tab{parent_doc}`.{date_field}
between %s and %s and `tab{parent_doc}`.docstatus = 1
{condition} group by `tab{child_doc}`.name
""".format(parent_doc = doctype,

View File

@ -93,6 +93,7 @@ def check_if_in_list(gle, gl_map, dimensions=None):
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
if not from_repost:
validate_account_for_perpetual_inventory(gl_map)
validate_cwip_accounts(gl_map)
round_off_debit_credit(gl_map)
@ -123,6 +124,16 @@ def validate_account_for_perpetual_inventory(gl_map):
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
.format(entry.account), StockAccountInvalidTransaction)
def validate_cwip_accounts(gl_map):
if not cint(frappe.db.get_value("Asset Settings", None, "disable_cwip_accounting")) \
and gl_map[0].voucher_type == "Journal Entry":
cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
where account_type = 'Capital Work in Progress' and is_group=0""")]
for entry in gl_map:
if entry.account in cwip_accounts:
frappe.throw(_("Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
def round_off_debit_credit(gl_map):
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))

View File

@ -30,7 +30,9 @@ def update_last_purchase_rate(doc, is_submit):
# for it to be considered for latest purchase rate
if flt(d.conversion_factor):
last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor)
else:
# Check if item code is present
# Conversion factor should not be mandatory for non itemized items
elif d.item_code:
frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
# update last purchsae rate
@ -84,13 +86,13 @@ def get_linked_material_requests(items):
items = json.loads(items)
mr_list = []
for item in items:
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
(mr_item.qty - mr_item.ordered_qty) AS qty,
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
(mr_item.qty - mr_item.ordered_qty) AS qty,
mr_item.item_code AS item_code,
mr_item.name AS mr_item
mr_item.name AS mr_item
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
WHERE mr.name = mr_item.parent
AND mr_item.item_code = %(item)s
AND mr_item.item_code = %(item)s
AND mr.material_request_type = 'Purchase'
AND mr.per_ordered < 99.99
AND mr.docstatus = 1
@ -98,6 +100,6 @@ def get_linked_material_requests(items):
ORDER BY mr_item.item_code ASC""",{"item": item}, as_dict=1)
if material_request:
mr_list.append(material_request)
return mr_list

View File

@ -94,6 +94,13 @@ def get_data():
"name": "BOM Update Tool",
"description": _("Replace BOM and update latest price in all BOMs"),
},
{
"type": "page",
"label": _("BOM Comparison Tool"),
"name": "bom-comparison-tool",
"description": _("Compare BOMs for changes in Raw Materials and Operations"),
"data_doctype": "BOM"
},
]
},
{

View File

@ -1192,6 +1192,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
.format(child_item.idx, child_item.item_code))
else:
child_item.rate = flt(d.get("rate"))
if child_item.price_list_rate:
child_item.discount_percentage = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0, \
child_item.precision("discount_percentage"))
child_item.flags.ignore_validate_update_after_submit = True
if new_child_flag:
child_item.idx = len(parent.items) + 1

View File

@ -395,7 +395,9 @@ class BuyingController(StockController):
def set_qty_as_per_stock_uom(self):
for d in self.get("items"):
if d.meta.get_field("stock_qty"):
if not d.conversion_factor:
# Check if item code is present
# Conversion factor should not be mandatory for non itemized items
if not d.conversion_factor and d.item_code:
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)

View File

@ -82,7 +82,7 @@ class calculate_taxes_and_totals(object):
item.net_rate = item.rate
if not item.qty and self.doc.is_return:
if not item.qty and self.doc.get("is_return"):
item.amount = flt(-1 * item.rate, item.precision("amount"))
else:
item.amount = flt(item.rate * item.qty, item.precision("amount"))

View File

@ -77,7 +77,7 @@ def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_len
if or_filters:
for r in frappe.get_list(doctype, fields=fields,filters=filters, or_filters=or_filters,
limit_start=limit_start, limit_page_length=limit_page_length,
limit_start=limit_start, limit_page_length=limit_page_length,
ignore_permissions=ignore_permissions, order_by=order_by):
data.append(r)
@ -130,38 +130,56 @@ def get_customers_suppliers(doctype, user):
suppliers = []
meta = frappe.get_meta(doctype)
customer_field_name = get_customer_field_name(doctype)
has_customer_field = meta.has_field(customer_field_name)
has_supplier_field = meta.has_field('supplier')
if has_common(["Supplier", "Customer"], frappe.get_roles(user)):
contacts = frappe.db.sql("""
select
select
`tabContact`.email_id,
`tabDynamic Link`.link_doctype,
`tabDynamic Link`.link_name
from
from
`tabContact`, `tabDynamic Link`
where
`tabContact`.name=`tabDynamic Link`.parent and `tabContact`.email_id =%s
""", user, as_dict=1)
customers = [c.link_name for c in contacts if c.link_doctype == 'Customer'] \
if meta.get_field("customer") else None
suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier'] \
if meta.get_field("supplier") else None
customers = [c.link_name for c in contacts if c.link_doctype == 'Customer']
suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier']
elif frappe.has_permission(doctype, 'read', user=user):
customers = [customer.name for customer in frappe.get_list("Customer")] \
if meta.get_field("customer") else None
suppliers = [supplier.name for supplier in frappe.get_list("Customer")] \
if meta.get_field("supplier") else None
customer_list = frappe.get_list("Customer")
customers = suppliers = [customer.name for customer in customer_list]
return customers, suppliers
return customers if has_customer_field else None, \
suppliers if has_supplier_field else None
def has_website_permission(doc, ptype, user, verbose=False):
doctype = doc.doctype
customers, suppliers = get_customers_suppliers(doctype, user)
if customers:
return frappe.get_all(doctype, filters=[(doctype, "customer", "in", customers),
(doctype, "name", "=", doc.name)]) and True or False
return frappe.db.exists(doctype, filters=get_customer_filter(doc, customers))
elif suppliers:
fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier'
return frappe.get_all(doctype, filters=[(doctype, fieldname, "in", suppliers),
(doctype, "name", "=", doc.name)]) and True or False
return frappe.db.exists(doctype, filters={
'name': doc.name,
fieldname: ["in", suppliers]
})
else:
return False
def get_customer_filter(doc, customers):
doctype = doc.doctype
filters = frappe._dict()
filters.name = doc.name
filters[get_customer_field_name(doctype)] = ['in', customers]
if doctype == 'Quotation':
filters.party_type = 'Customer'
return filters
def get_customer_field_name(doctype):
if doctype == 'Quotation':
return 'party_name'
else:
return 'customer'

View File

@ -64,13 +64,20 @@ class EmployeeAdvance(Document):
def update_claimed_amount(self):
claimed_amount = frappe.db.sql("""
select sum(ifnull(allocated_amount, 0))
from `tabExpense Claim Advance`
where employee_advance = %s and docstatus=1 and allocated_amount > 0
SELECT sum(ifnull(allocated_amount, 0))
FROM `tabExpense Claim Advance` eca, `tabExpense Claim` ec
WHERE
eca.employee_advance = %s
AND ec.approval_status="Approved"
AND ec.name = eca.parent
AND ec.docstatus=1
AND eca.allocated_amount > 0
""", self.name)[0][0] or 0
if claimed_amount:
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
self.reload()
self.set_status()
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
@frappe.whitelist()
def get_due_advance_amount(employee, posting_date):

View File

@ -183,7 +183,7 @@ frappe.ui.form.on("Expense Claim", {
refresh: function(frm) {
frm.trigger("toggle_fields");
if(frm.doc.docstatus == 1) {
if(frm.doc.docstatus === 1 && frm.doc.approval_status !== "Rejected") {
frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
@ -194,7 +194,7 @@ frappe.ui.form.on("Expense Claim", {
}, __("View"));
}
if (frm.doc.docstatus===1
if (frm.doc.docstatus===1 && !cint(frm.doc.is_paid) && cint(frm.doc.grand_total) > 0
&& (cint(frm.doc.total_amount_reimbursed) < cint(frm.doc.total_sanctioned_amount))
&& frappe.model.can_create("Payment Entry")) {
frm.add_custom_button(__('Payment'),

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@ from erpnext.setup.utils import get_exchange_rate
from frappe.website.website_generator import WebsiteGenerator
from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.stock.get_item_details import get_price_list_rate
from frappe.core.doctype.version.version import get_diff
import functools
@ -763,3 +764,52 @@ def add_additional_cost(stock_entry, work_order):
'description': name[0],
'amount': items.get(name[0])
})
@frappe.whitelist()
def get_bom_diff(bom1, bom2):
from frappe.model import table_fields
doc1 = frappe.get_doc('BOM', bom1)
doc2 = frappe.get_doc('BOM', bom2)
out = get_diff(doc1, doc2)
out.row_changed = []
out.added = []
out.removed = []
meta = doc1.meta
identifiers = {
'operations': 'operation',
'items': 'item_code',
'scrap_items': 'item_code',
'exploded_items': 'item_code'
}
for df in meta.fields:
old_value, new_value = doc1.get(df.fieldname), doc2.get(df.fieldname)
if df.fieldtype in table_fields:
identifier = identifiers[df.fieldname]
# make maps
old_row_by_identifier, new_row_by_identifier = {}, {}
for d in old_value:
old_row_by_identifier[d.get(identifier)] = d
for d in new_value:
new_row_by_identifier[d.get(identifier)] = d
# check rows for additions, changes
for i, d in enumerate(new_value):
if d.get(identifier) in old_row_by_identifier:
diff = get_diff(old_row_by_identifier[d.get(identifier)], d, for_child=True)
if diff and diff.changed:
out.row_changed.append((df.fieldname, i, d.get(identifier), diff.changed))
else:
out.added.append([df.fieldname, d.as_dict()])
# check for deletions
for d in old_value:
if not d.get(identifier) in new_row_by_identifier:
out.removed.append([df.fieldname, d.as_dict()])
return out

View File

@ -320,7 +320,8 @@ class ProductionPlan(Document):
'qty': data.get("stock_qty") * item.get("qty"),
'production_plan': self.name,
'company': self.company,
'fg_warehouse': item.get("fg_warehouse")
'fg_warehouse': item.get("fg_warehouse"),
'update_consumed_material_cost_in_project': 0
})
work_order = self.create_work_order(data)
@ -430,7 +431,7 @@ def download_raw_materials(production_plan):
continue
item_list.append(['', '', '', '', bin_dict.get('warehouse'),
bin_dict.get('projected_qty'), bin_dict.get('actual_qty')])
bin_dict.get('projected_qty', 0), bin_dict.get('actual_qty', 0)])
build_csv_response(item_list, doc.name)
@ -507,8 +508,8 @@ def get_material_request_items(row, sales_order,
required_qty = 0
if ignore_existing_ordered_qty or bin_dict.get("projected_qty", 0) < 0:
required_qty = total_qty
elif total_qty > bin_dict.get("projected_qty"):
required_qty = total_qty - bin_dict.get("projected_qty")
elif total_qty > bin_dict.get("projected_qty", 0):
required_qty = total_qty - bin_dict.get("projected_qty", 0)
if required_qty > 0 and required_qty < row['min_order_qty']:
required_qty = row['min_order_qty']
item_group_defaults = get_item_group_defaults(row.item_code, company)

View File

@ -1,484 +1,504 @@
{
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-01-10 16:34:16",
"doctype": "DocType",
"document_type": "Setup",
"field_order": [
"item",
"naming_series",
"status",
"production_item",
"item_name",
"image",
"bom_no",
"allow_alternative_item",
"use_multi_level_bom",
"skip_transfer",
"column_break1",
"company",
"qty",
"material_transferred_for_manufacturing",
"produced_qty",
"sales_order",
"project",
"from_wip_warehouse",
"warehouses",
"wip_warehouse",
"fg_warehouse",
"column_break_12",
"scrap_warehouse",
"required_items_section",
"required_items",
"time",
"planned_start_date",
"actual_start_date",
"column_break_13",
"planned_end_date",
"actual_end_date",
"expected_delivery_date",
"operations_section",
"transfer_material_against",
"operations",
"section_break_22",
"planned_operating_cost",
"actual_operating_cost",
"additional_operating_cost",
"column_break_24",
"total_operating_cost",
"more_info",
"description",
"stock_uom",
"column_break2",
"material_request",
"material_request_item",
"sales_order_item",
"production_plan",
"production_plan_item",
"product_bundle_item",
"amended_from"
],
"fields": [
{
"fieldname": "item",
"fieldtype": "Section Break",
"options": "fa fa-gift"
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"options": "MFG-WO-.YYYY.-",
"print_hide": 1,
"reqd": 1,
"set_only_once": 1
},
{
"default": "Draft",
"depends_on": "eval:!doc.__islocal",
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled",
"read_only": 1,
"reqd": 1,
"search_index": 1
},
{
"fieldname": "production_item",
"fieldtype": "Link",
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Item To Manufacture",
"oldfieldname": "production_item",
"oldfieldtype": "Link",
"options": "Item",
"reqd": 1
},
{
"depends_on": "eval:doc.production_item",
"fieldname": "item_name",
"fieldtype": "Data",
"label": "Item Name",
"read_only": 1
},
{
"fetch_from": "production_item.image",
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
"label": "Image",
"options": "image",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "bom_no",
"fieldtype": "Link",
"label": "BOM No",
"oldfieldname": "bom_no",
"oldfieldtype": "Link",
"options": "BOM",
"reqd": 1
},
{
"default": "0",
"fieldname": "allow_alternative_item",
"fieldtype": "Check",
"label": "Allow Alternative Item"
},
{
"default": "1",
"description": "Plan material for sub-assemblies",
"fieldname": "use_multi_level_bom",
"fieldtype": "Check",
"label": "Use Multi-Level BOM",
"print_hide": 1
},
{
"default": "0",
"description": "Check if material transfer entry is not required",
"fieldname": "skip_transfer",
"fieldtype": "Check",
"label": "Skip Material Transfer to WIP Warehouse"
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"width": "50%"
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"remember_last_selected_value": 1,
"reqd": 1
},
{
"fieldname": "qty",
"fieldtype": "Float",
"label": "Qty To Manufacture",
"oldfieldname": "qty",
"oldfieldtype": "Currency",
"reqd": 1
},
{
"default": "0",
"depends_on": "eval:doc.docstatus==1 && doc.skip_transfer==0",
"fieldname": "material_transferred_for_manufacturing",
"fieldtype": "Float",
"label": "Material Transferred for Manufacturing",
"no_copy": 1,
"read_only": 1
},
{
"default": "0",
"depends_on": "eval:doc.docstatus==1",
"fieldname": "produced_qty",
"fieldtype": "Float",
"label": "Manufactured Qty",
"no_copy": 1,
"oldfieldname": "produced_qty",
"oldfieldtype": "Currency",
"read_only": 1
},
{
"allow_on_submit": 1,
"fieldname": "sales_order",
"fieldtype": "Link",
"in_global_search": 1,
"label": "Sales Order",
"options": "Sales Order"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"oldfieldname": "project",
"oldfieldtype": "Link",
"options": "Project"
},
{
"default": "0",
"depends_on": "skip_transfer",
"fieldname": "from_wip_warehouse",
"fieldtype": "Check",
"label": "Backflush Raw Materials From Work-in-Progress Warehouse"
},
{
"fieldname": "warehouses",
"fieldtype": "Section Break",
"label": "Warehouses",
"options": "fa fa-building"
},
{
"fieldname": "wip_warehouse",
"fieldtype": "Link",
"label": "Work-in-Progress Warehouse",
"options": "Warehouse"
},
{
"fieldname": "fg_warehouse",
"fieldtype": "Link",
"label": "Target Warehouse",
"options": "Warehouse"
},
{
"fieldname": "column_break_12",
"fieldtype": "Column Break"
},
{
"fieldname": "scrap_warehouse",
"fieldtype": "Link",
"label": "Scrap Warehouse",
"options": "Warehouse"
},
{
"fieldname": "required_items_section",
"fieldtype": "Section Break",
"label": "Required Items"
},
{
"fieldname": "required_items",
"fieldtype": "Table",
"label": "Required Items",
"no_copy": 1,
"options": "Work Order Item",
"print_hide": 1
},
{
"fieldname": "time",
"fieldtype": "Section Break",
"label": "Time",
"options": "fa fa-time"
},
{
"allow_on_submit": 1,
"default": "now",
"fieldname": "planned_start_date",
"fieldtype": "Datetime",
"label": "Planned Start Date",
"reqd": 1
},
{
"fieldname": "actual_start_date",
"fieldtype": "Datetime",
"label": "Actual Start Date",
"read_only": 1
},
{
"fieldname": "column_break_13",
"fieldtype": "Column Break"
},
{
"fieldname": "planned_end_date",
"fieldtype": "Datetime",
"label": "Planned End Date",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "actual_end_date",
"fieldtype": "Datetime",
"label": "Actual End Date",
"read_only": 1
},
{
"allow_on_submit": 1,
"fieldname": "expected_delivery_date",
"fieldtype": "Date",
"label": "Expected Delivery Date"
},
{
"fieldname": "operations_section",
"fieldtype": "Section Break",
"label": "Operations",
"options": "fa fa-wrench"
},
{
"default": "Work Order",
"depends_on": "operations",
"fieldname": "transfer_material_against",
"fieldtype": "Select",
"label": "Transfer Material Against",
"options": "\nWork Order\nJob Card"
},
{
"fieldname": "operations",
"fieldtype": "Table",
"label": "Operations",
"options": "Work Order Operation",
"read_only": 1
},
{
"depends_on": "operations",
"fieldname": "section_break_22",
"fieldtype": "Section Break",
"label": "Operation Cost"
},
{
"fieldname": "planned_operating_cost",
"fieldtype": "Currency",
"label": "Planned Operating Cost",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "actual_operating_cost",
"fieldtype": "Currency",
"label": "Actual Operating Cost",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "additional_operating_cost",
"fieldtype": "Currency",
"label": "Additional Operating Cost",
"no_copy": 1,
"options": "Company:company:default_currency"
},
{
"fieldname": "column_break_24",
"fieldtype": "Column Break"
},
{
"fieldname": "total_operating_cost",
"fieldtype": "Currency",
"label": "Total Operating Cost",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Information",
"options": "fa fa-file-text"
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Item Description",
"read_only": 1
},
{
"fieldname": "stock_uom",
"fieldtype": "Link",
"label": "Stock UOM",
"oldfieldname": "stock_uom",
"oldfieldtype": "Data",
"options": "UOM",
"read_only": 1
},
{
"fieldname": "column_break2",
"fieldtype": "Column Break",
"width": "50%"
},
{
"description": "Manufacture against Material Request",
"fieldname": "material_request",
"fieldtype": "Link",
"label": "Material Request",
"options": "Material Request"
},
{
"fieldname": "material_request_item",
"fieldtype": "Data",
"hidden": 1,
"label": "Material Request Item",
"read_only": 1
},
{
"fieldname": "sales_order_item",
"fieldtype": "Data",
"hidden": 1,
"label": "Sales Order Item",
"read_only": 1
},
{
"fieldname": "production_plan",
"fieldtype": "Link",
"label": "Production Plan",
"no_copy": 1,
"options": "Production Plan",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "production_plan_item",
"fieldtype": "Data",
"label": "Production Plan Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "product_bundle_item",
"fieldtype": "Link",
"label": "Product Bundle Item",
"no_copy": 1,
"options": "Item",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Amended From",
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Work Order",
"read_only": 1
}
],
"icon": "fa fa-cogs",
"idx": 1,
"image_field": "image",
"is_submittable": 1,
"modified": "2019-05-27 09:36:16.707719",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"import": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing User",
"set_user_permissions": 1,
"share": 1,
"submit": 1,
"write": 1
},
{
"read": 1,
"report": 1,
"role": "Stock User"
}
],
"sort_order": "ASC",
"title_field": "production_item",
"track_changes": 1,
"track_seen": 1
}
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-01-10 16:34:16",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"item",
"naming_series",
"status",
"production_item",
"item_name",
"image",
"bom_no",
"column_break1",
"company",
"qty",
"material_transferred_for_manufacturing",
"produced_qty",
"sales_order",
"project",
"settings_section",
"allow_alternative_item",
"use_multi_level_bom",
"column_break_18",
"skip_transfer",
"from_wip_warehouse",
"update_consumed_material_cost_in_project",
"warehouses",
"wip_warehouse",
"fg_warehouse",
"column_break_12",
"scrap_warehouse",
"required_items_section",
"required_items",
"time",
"planned_start_date",
"actual_start_date",
"column_break_13",
"planned_end_date",
"actual_end_date",
"expected_delivery_date",
"operations_section",
"transfer_material_against",
"operations",
"section_break_22",
"planned_operating_cost",
"actual_operating_cost",
"additional_operating_cost",
"column_break_24",
"total_operating_cost",
"more_info",
"description",
"stock_uom",
"column_break2",
"material_request",
"material_request_item",
"sales_order_item",
"production_plan",
"production_plan_item",
"product_bundle_item",
"amended_from"
],
"fields": [
{
"fieldname": "item",
"fieldtype": "Section Break",
"options": "fa fa-gift"
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"options": "MFG-WO-.YYYY.-",
"print_hide": 1,
"reqd": 1,
"set_only_once": 1
},
{
"default": "Draft",
"depends_on": "eval:!doc.__islocal",
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled",
"read_only": 1,
"reqd": 1,
"search_index": 1
},
{
"fieldname": "production_item",
"fieldtype": "Link",
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Item To Manufacture",
"oldfieldname": "production_item",
"oldfieldtype": "Link",
"options": "Item",
"reqd": 1
},
{
"depends_on": "eval:doc.production_item",
"fieldname": "item_name",
"fieldtype": "Data",
"label": "Item Name",
"read_only": 1
},
{
"fetch_from": "production_item.image",
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
"label": "Image",
"options": "image",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "bom_no",
"fieldtype": "Link",
"label": "BOM No",
"oldfieldname": "bom_no",
"oldfieldtype": "Link",
"options": "BOM",
"reqd": 1
},
{
"default": "0",
"fieldname": "allow_alternative_item",
"fieldtype": "Check",
"label": "Allow Alternative Item"
},
{
"default": "1",
"description": "Plan material for sub-assemblies",
"fieldname": "use_multi_level_bom",
"fieldtype": "Check",
"label": "Use Multi-Level BOM",
"print_hide": 1
},
{
"default": "0",
"description": "Check if material transfer entry is not required",
"fieldname": "skip_transfer",
"fieldtype": "Check",
"label": "Skip Material Transfer to WIP Warehouse"
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"width": "50%"
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"remember_last_selected_value": 1,
"reqd": 1
},
{
"fieldname": "qty",
"fieldtype": "Float",
"label": "Qty To Manufacture",
"oldfieldname": "qty",
"oldfieldtype": "Currency",
"reqd": 1
},
{
"default": "0",
"depends_on": "eval:doc.docstatus==1 && doc.skip_transfer==0",
"fieldname": "material_transferred_for_manufacturing",
"fieldtype": "Float",
"label": "Material Transferred for Manufacturing",
"no_copy": 1,
"read_only": 1
},
{
"default": "0",
"depends_on": "eval:doc.docstatus==1",
"fieldname": "produced_qty",
"fieldtype": "Float",
"label": "Manufactured Qty",
"no_copy": 1,
"oldfieldname": "produced_qty",
"oldfieldtype": "Currency",
"read_only": 1
},
{
"allow_on_submit": 1,
"fieldname": "sales_order",
"fieldtype": "Link",
"in_global_search": 1,
"label": "Sales Order",
"options": "Sales Order"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"oldfieldname": "project",
"oldfieldtype": "Link",
"options": "Project"
},
{
"default": "0",
"depends_on": "skip_transfer",
"fieldname": "from_wip_warehouse",
"fieldtype": "Check",
"label": "Backflush Raw Materials From Work-in-Progress Warehouse"
},
{
"fieldname": "warehouses",
"fieldtype": "Section Break",
"label": "Warehouses",
"options": "fa fa-building"
},
{
"fieldname": "wip_warehouse",
"fieldtype": "Link",
"label": "Work-in-Progress Warehouse",
"options": "Warehouse"
},
{
"fieldname": "fg_warehouse",
"fieldtype": "Link",
"label": "Target Warehouse",
"options": "Warehouse"
},
{
"fieldname": "column_break_12",
"fieldtype": "Column Break"
},
{
"fieldname": "scrap_warehouse",
"fieldtype": "Link",
"label": "Scrap Warehouse",
"options": "Warehouse"
},
{
"fieldname": "required_items_section",
"fieldtype": "Section Break",
"label": "Required Items"
},
{
"fieldname": "required_items",
"fieldtype": "Table",
"label": "Required Items",
"no_copy": 1,
"options": "Work Order Item",
"print_hide": 1
},
{
"fieldname": "time",
"fieldtype": "Section Break",
"label": "Time",
"options": "fa fa-time"
},
{
"allow_on_submit": 1,
"default": "now",
"fieldname": "planned_start_date",
"fieldtype": "Datetime",
"label": "Planned Start Date",
"reqd": 1
},
{
"fieldname": "actual_start_date",
"fieldtype": "Datetime",
"label": "Actual Start Date",
"read_only": 1
},
{
"fieldname": "column_break_13",
"fieldtype": "Column Break"
},
{
"fieldname": "planned_end_date",
"fieldtype": "Datetime",
"label": "Planned End Date",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "actual_end_date",
"fieldtype": "Datetime",
"label": "Actual End Date",
"read_only": 1
},
{
"allow_on_submit": 1,
"fieldname": "expected_delivery_date",
"fieldtype": "Date",
"label": "Expected Delivery Date"
},
{
"fieldname": "operations_section",
"fieldtype": "Section Break",
"label": "Operations",
"options": "fa fa-wrench"
},
{
"default": "Work Order",
"depends_on": "operations",
"fieldname": "transfer_material_against",
"fieldtype": "Select",
"label": "Transfer Material Against",
"options": "\nWork Order\nJob Card"
},
{
"fieldname": "operations",
"fieldtype": "Table",
"label": "Operations",
"options": "Work Order Operation",
"read_only": 1
},
{
"depends_on": "operations",
"fieldname": "section_break_22",
"fieldtype": "Section Break",
"label": "Operation Cost"
},
{
"fieldname": "planned_operating_cost",
"fieldtype": "Currency",
"label": "Planned Operating Cost",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "actual_operating_cost",
"fieldtype": "Currency",
"label": "Actual Operating Cost",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "additional_operating_cost",
"fieldtype": "Currency",
"label": "Additional Operating Cost",
"no_copy": 1,
"options": "Company:company:default_currency"
},
{
"fieldname": "column_break_24",
"fieldtype": "Column Break"
},
{
"fieldname": "total_operating_cost",
"fieldtype": "Currency",
"label": "Total Operating Cost",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Information",
"options": "fa fa-file-text"
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Item Description",
"read_only": 1
},
{
"fieldname": "stock_uom",
"fieldtype": "Link",
"label": "Stock UOM",
"oldfieldname": "stock_uom",
"oldfieldtype": "Data",
"options": "UOM",
"read_only": 1
},
{
"fieldname": "column_break2",
"fieldtype": "Column Break",
"width": "50%"
},
{
"description": "Manufacture against Material Request",
"fieldname": "material_request",
"fieldtype": "Link",
"label": "Material Request",
"options": "Material Request"
},
{
"fieldname": "material_request_item",
"fieldtype": "Data",
"hidden": 1,
"label": "Material Request Item",
"read_only": 1
},
{
"fieldname": "sales_order_item",
"fieldtype": "Data",
"hidden": 1,
"label": "Sales Order Item",
"read_only": 1
},
{
"fieldname": "production_plan",
"fieldtype": "Link",
"label": "Production Plan",
"no_copy": 1,
"options": "Production Plan",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "production_plan_item",
"fieldtype": "Data",
"label": "Production Plan Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "product_bundle_item",
"fieldtype": "Link",
"label": "Product Bundle Item",
"no_copy": 1,
"options": "Item",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Amended From",
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Work Order",
"read_only": 1
},
{
"fieldname": "settings_section",
"fieldtype": "Section Break",
"label": "Settings"
},
{
"fieldname": "column_break_18",
"fieldtype": "Column Break"
},
{
"default": "1",
"fieldname": "update_consumed_material_cost_in_project",
"fieldtype": "Check",
"label": "Update Consumed Material Cost In Project"
}
],
"icon": "fa fa-cogs",
"idx": 1,
"image_field": "image",
"is_submittable": 1,
"modified": "2019-07-31 00:13:38.218277",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"import": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing User",
"set_user_permissions": 1,
"share": 1,
"submit": 1,
"write": 1
},
{
"read": 1,
"report": 1,
"role": "Stock User"
}
],
"sort_field": "modified",
"sort_order": "ASC",
"title_field": "production_item",
"track_changes": 1,
"track_seen": 1
}

View File

@ -0,0 +1,213 @@
frappe.pages['bom-comparison-tool'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
title: __('BOM Comparison Tool'),
single_column: true
});
new erpnext.BOMComparisonTool(page);
}
erpnext.BOMComparisonTool = class BOMComparisonTool {
constructor(page) {
this.page = page;
this.make_form();
}
make_form() {
this.form = new frappe.ui.FieldGroup({
fields: [
{
label: __('BOM 1'),
fieldname: 'name1',
fieldtype: 'Link',
options: 'BOM',
change: () => this.fetch_and_render()
},
{
fieldtype: 'Column Break'
},
{
label: __('BOM 2'),
fieldname: 'name2',
fieldtype: 'Link',
options: 'BOM',
change: () => this.fetch_and_render()
},
{
fieldtype: 'Section Break'
},
{
fieldtype: 'HTML',
fieldname: 'preview'
}
],
body: this.page.body
});
this.form.make();
}
fetch_and_render() {
let { name1, name2 } = this.form.get_values();
if (!(name1 && name2)) {
this.form.get_field('preview').html('');
return;
}
// set working state
this.form.get_field('preview').html(`
<div class="text-muted margin-top">
${__("Fetching...")}
</div>
`);
frappe.call('erpnext.manufacturing.doctype.bom.bom.get_bom_diff', {
bom1: name1,
bom2: name2
}).then(r => {
let diff = r.message;
frappe.model.with_doctype('BOM', () => {
this.render('BOM', name1, name2, diff);
});
});
}
render(doctype, name1, name2, diff) {
let change_html = (title, doctype, changed) => {
let values_changed = this.get_changed_values(doctype, changed)
.map(change => {
let [fieldname, value1, value2] = change;
return `
<tr>
<td>${frappe.meta.get_label(doctype, fieldname)}</td>
<td>${value1}</td>
<td>${value2}</td>
</tr>
`;
})
.join('');
return `
<h4 class="margin-top">${title}</h4>
<div>
<table class="table table-bordered">
<tr>
<th width="33%">${__('Field')}</th>
<th width="33%">${name1}</th>
<th width="33%">${name2}</th>
</tr>
${values_changed}
</table>
</div>
`;
}
let value_changes = change_html(__('Values Changed'), doctype, diff.changed);
let row_changes_by_fieldname = group_items(diff.row_changed, change => change[0]);
let table_changes = Object.keys(row_changes_by_fieldname).map(fieldname => {
let changes = row_changes_by_fieldname[fieldname];
let df = frappe.meta.get_docfield(doctype, fieldname);
let html = changes.map(change => {
let [fieldname,, item_code, changes] = change;
let df = frappe.meta.get_docfield(doctype, fieldname);
let child_doctype = df.options;
let values_changed = this.get_changed_values(child_doctype, changes);
return values_changed.map((change, i) => {
let [fieldname, value1, value2] = change;
let th = i === 0
? `<th rowspan="${values_changed.length}">${item_code}</th>`
: '';
return `
<tr>
${th}
<td>${frappe.meta.get_label(child_doctype, fieldname)}</td>
<td>${value1}</td>
<td>${value2}</td>
</tr>
`;
}).join('');
}).join('');
return `
<h4 class="margin-top">${__('Changes in {0}', [df.label])}</h4>
<table class="table table-bordered">
<tr>
<th width="25%">${__('Item Code')}</th>
<th width="25%">${__('Field')}</th>
<th width="25%">${name1}</th>
<th width="25%">${name2}</th>
</tr>
${html}
</table>
`;
}).join('');
let get_added_removed_html = (title, grouped_items) => {
return Object.keys(grouped_items).map(fieldname => {
let rows = grouped_items[fieldname];
let df = frappe.meta.get_docfield(doctype, fieldname);
let fields = frappe.meta.get_docfields(df.options)
.filter(df => df.in_list_view);
let html = rows.map(row => {
let [, doc] = row;
let cells = fields
.map(df => `<td>${doc[df.fieldname]}</td>`)
.join('');
return `<tr>${cells}</tr>`;
}).join('');
let header = fields.map(df => `<th>${df.label}</th>`).join('');
return `
<h4 class="margin-top">${$.format(title, [df.label])}</h4>
<table class="table table-bordered">
<tr>${header}</tr>
${html}
</table>
`;
}).join('');
};
let added_by_fieldname = group_items(diff.added, change => change[0]);
let removed_by_fieldname = group_items(diff.removed, change => change[0]);
let added_html = get_added_removed_html(__('Rows Added in {0}'), added_by_fieldname);
let removed_html = get_added_removed_html(__('Rows Removed in {0}'), removed_by_fieldname);
let html = `
${value_changes}
${table_changes}
${added_html}
${removed_html}
`;
this.form.get_field('preview').html(html);
}
get_changed_values(doctype, changed) {
return changed.filter(change => {
let [fieldname, value1, value2] = change;
if (!value1) value1 = '';
if (!value2) value2 = '';
if (value1 === value2) return false;
let df = frappe.meta.get_docfield(doctype, fieldname);
if (!df) return false;
if (df.hidden) return false;
return true;
});
}
};
function group_items(array, fn) {
return array.reduce((acc, item) => {
let key = fn(item);
acc[key] = acc[key] || [];
acc[key].push(item);
return acc;
}, {});
}

View File

@ -0,0 +1,30 @@
{
"content": null,
"creation": "2019-07-29 13:24:38.201981",
"docstatus": 0,
"doctype": "Page",
"idx": 0,
"modified": "2019-07-29 13:24:38.201981",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "bom-comparison-tool",
"owner": "Administrator",
"page_name": "BOM Comparison Tool",
"restrict_to_domain": "Manufacturing",
"roles": [
{
"role": "System Manager"
},
{
"role": "Manufacturing User"
},
{
"role": "Manufacturing Manager"
}
],
"script": null,
"standard": "Yes",
"style": null,
"system_page": 0,
"title": "BOM Comparison Tool"
}

View File

@ -60,7 +60,15 @@ $.extend(erpnext, {
var me = this;
$btn.on("click", function() {
me.show_serial_batch_selector(grid_row.frm, grid_row.doc);
let callback = '';
let on_close = '';
if (grid_row.doc.serial_no) {
grid_row.doc.has_serial_no = true;
}
me.show_serial_batch_selector(grid_row.frm, grid_row.doc,
callback, on_close, true);
});
},
});

View File

@ -255,27 +255,44 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
});
return;
} else {
var fields = [
{fieldtype:'Table', fieldname: 'items',
description: __('Select BOM and Qty for Production'),
fields: [
{fieldtype:'Read Only', fieldname:'item_code',
label: __('Item Code'), in_list_view:1},
{fieldtype:'Link', fieldname:'bom', options: 'BOM', reqd: 1,
label: __('Select BOM'), in_list_view:1, get_query: function(doc) {
return {filters: {item: doc.item_code}};
}},
{fieldtype:'Float', fieldname:'pending_qty', reqd: 1,
label: __('Qty'), in_list_view:1},
{fieldtype:'Data', fieldname:'sales_order_item', reqd: 1,
label: __('Sales Order Item'), hidden:1}
],
data: r.message,
get_data: function() {
return r.message
const fields = [{
label: 'Items',
fieldtype: 'Table',
fieldname: 'items',
description: __('Select BOM and Qty for Production'),
fields: [{
fieldtype: 'Read Only',
fieldname: 'item_code',
label: __('Item Code'),
in_list_view: 1
}, {
fieldtype: 'Link',
fieldname: 'bom',
options: 'BOM',
reqd: 1,
label: __('Select BOM'),
in_list_view: 1,
get_query: function (doc) {
return { filters: { item: doc.item_code } };
}
}, {
fieldtype: 'Float',
fieldname: 'pending_qty',
reqd: 1,
label: __('Qty'),
in_list_view: 1
}, {
fieldtype: 'Data',
fieldname: 'sales_order_item',
reqd: 1,
label: __('Sales Order Item'),
hidden: 1
}],
data: r.message,
get_data: () => {
return r.message
}
]
}]
var d = new frappe.ui.Dialog({
title: __('Select Items to Manufacture'),
fields: fields,

View File

@ -251,13 +251,12 @@ def _get_cart_quotation(party=None):
if quotation:
qdoc = frappe.get_doc("Quotation", quotation[0].name)
else:
[company, price_list] = frappe.db.get_value("Shopping Cart Settings", None, ["company", "price_list"])
company = frappe.db.get_value("Shopping Cart Settings", None, ["company"])
qdoc = frappe.get_doc({
"doctype": "Quotation",
"naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-",
"quotation_to": party.doctype,
"company": company,
"selling_price_list": price_list,
"order_type": "Shopping Cart",
"status": "Draft",
"docstatus": 0,

View File

@ -145,6 +145,10 @@ class StockEntry(StockController):
self.precision("transfer_qty", item))
def update_cost_in_project(self):
if (self.work_order and not frappe.db.get_value("Work Order",
self.work_order, "update_consumed_material_cost_in_project")):
return
if self.project:
amount = frappe.db.sql(""" select ifnull(sum(sed.amount), 0)
from

3
package-lock.json generated
View File

@ -1,3 +0,0 @@
{
"lockfileVersion": 1
}