Fixed Test Cases
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index c37f075..5c9b5b6 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -29,6 +29,7 @@
["_Test Account S&H Education Cess", "_Test Account Tax Assets", "Ledger", "Tax"],
["_Test Account CST", "Direct Expenses", "Ledger", "Tax"],
["_Test Account Discount", "Direct Expenses", "Ledger", None],
+ ["_Test Write Off", "Indirect Expenses", "Ledger", None],
# related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets", "Ledger", None],
diff --git a/erpnext/accounts/doctype/cost_center/test_records.json b/erpnext/accounts/doctype/cost_center/test_records.json
index 13e0ea9..7486214 100644
--- a/erpnext/accounts/doctype/cost_center/test_records.json
+++ b/erpnext/accounts/doctype/cost_center/test_records.json
@@ -2,25 +2,32 @@
{
"budgets": [
{
- "account": "_Test Account Cost for Goods Sold - _TC",
- "budget_allocated": 100000,
- "doctype": "Budget Detail",
- "fiscal_year": "_Test Fiscal Year 2013",
+ "account": "_Test Account Cost for Goods Sold - _TC",
+ "budget_allocated": 100000,
+ "doctype": "Budget Detail",
+ "fiscal_year": "_Test Fiscal Year 2013",
"parentfield": "budgets"
}
- ],
- "company": "_Test Company",
- "cost_center_name": "_Test Cost Center",
- "distribution_id": "_Test Distribution",
- "doctype": "Cost Center",
- "group_or_ledger": "Ledger",
+ ],
+ "company": "_Test Company",
+ "cost_center_name": "_Test Cost Center",
+ "distribution_id": "_Test Distribution",
+ "doctype": "Cost Center",
+ "group_or_ledger": "Ledger",
"parent_cost_center": "_Test Company - _TC"
},
{
"company": "_Test Company",
- "cost_center_name": "_Test Cost Center 2",
+ "cost_center_name": "_Test Cost Center 2",
+ "doctype": "Cost Center",
+ "group_or_ledger": "Ledger",
+ "parent_cost_center": "_Test Company - _TC"
+ },
+ {
+ "company": "_Test Company",
+ "cost_center_name": "_Test Write Off Cost Center",
"doctype": "Cost Center",
"group_or_ledger": "Ledger",
"parent_cost_center": "_Test Company - _TC"
}
-]
\ No newline at end of file
+]
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 445bed2..2a43814 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -15,6 +15,7 @@
test_record = {
"doctype": "Pricing Rule",
+ "title": "_Test Pricing Rule",
"apply_on": "Item Code",
"item_code": "_Test Item",
"selling": 1,
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_records.json b/erpnext/accounts/doctype/purchase_invoice/test_records.json
index 9139e0c..679e870 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_records.json
+++ b/erpnext/accounts/doctype/purchase_invoice/test_records.json
@@ -53,7 +53,7 @@
"description": "Shipping Charges",
"doctype": "Purchase Taxes and Charges",
"parentfield": "taxes",
- "rate": 100
+ "tax_amount": 100
},
{
"account_head": "_Test Account Customs Duty - _TC",
@@ -176,7 +176,7 @@
"description": "Shipping Charges",
"doctype": "Purchase Taxes and Charges",
"parentfield": "taxes",
- "rate": 100.0
+ "tax_amount": 100.0
},
{
"account_head": "_Test Account VAT - _TC",
@@ -187,7 +187,7 @@
"description": "VAT",
"doctype": "Purchase Taxes and Charges",
"parentfield": "taxes",
- "rate": 120.0
+ "tax_amount": 120.0
},
{
"account_head": "_Test Account Customs Duty - _TC",
@@ -198,7 +198,7 @@
"description": "Customs Duty",
"doctype": "Purchase Taxes and Charges",
"parentfield": "taxes",
- "rate": 150.0
+ "tax_amount": 150.0
}
],
"posting_date": "2013-02-03",
diff --git a/erpnext/accounts/doctype/sales_invoice/test_records.json b/erpnext/accounts/doctype/sales_invoice/test_records.json
index dc69339..7726835 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_records.json
+++ b/erpnext/accounts/doctype/sales_invoice/test_records.json
@@ -174,7 +174,7 @@
"description": "Shipping Charges",
"doctype": "Sales Taxes and Charges",
"parentfield": "taxes",
- "rate": 100
+ "tax_amount": 100
},
{
"account_head": "_Test Account Customs Duty - _TC",
@@ -367,7 +367,7 @@
"doctype": "Sales Taxes and Charges",
"idx": 7,
"parentfield": "taxes",
- "rate": 100
+ "tax_amount": 100
},
{
"account_head": "_Test Account Discount - _TC",
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index b9ea455..1ceedad 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -254,9 +254,9 @@
expected_values = {
"keys": ["price_list_rate", "discount_percentage", "rate", "amount",
- "base_price_list_rate", "base_rate", "base_amount"],
- "_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 50, 50, 499.98],
- "_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 150, 150, 750],
+ "base_price_list_rate", "base_rate", "base_amount", "net_rate", "net_amount"],
+ "_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 62.5, 62.5, 625.0, 50, 499.98],
+ "_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 190.66, 190.66, 953.3, 150, 750],
}
# check if children are saved
@@ -500,7 +500,9 @@
"naming_series": "_T-POS Setting-",
"selling_price_list": "_Test Price List",
"territory": "_Test Territory",
- "warehouse": "_Test Warehouse - _TC"
+ "warehouse": "_Test Warehouse - _TC",
+ "write_off_account": "_Test Write Off - _TC",
+ "write_off_cost_center": "_Test Write Off Cost Center - _TC"
})
if not frappe.db.exists("POS Setting", "_Test POS Setting"):
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_master/test_records.json b/erpnext/accounts/doctype/sales_taxes_and_charges_master/test_records.json
index dd9c595..12d4fca 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_master/test_records.json
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_master/test_records.json
@@ -1,157 +1,157 @@
[
{
- "company": "_Test Company",
- "doctype": "Sales Taxes and Charges Master",
+ "company": "_Test Company",
+ "doctype": "Sales Taxes and Charges Master",
"taxes": [
{
- "account_head": "_Test Account VAT - _TC",
- "charge_type": "On Net Total",
- "description": "VAT",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
"rate": 6
- },
+ },
{
- "account_head": "_Test Account Service Tax - _TC",
- "charge_type": "On Net Total",
- "description": "Service Tax",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
+ "account_head": "_Test Account Service Tax - _TC",
+ "charge_type": "On Net Total",
+ "description": "Service Tax",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
"rate": 6.36
}
- ],
- "title": "_Test Sales Taxes and Charges Master",
+ ],
+ "title": "_Test Sales Taxes and Charges Master",
"territories": [
{
- "doctype": "Applicable Territory",
- "parentfield": "territories",
+ "doctype": "Applicable Territory",
+ "parentfield": "territories",
"territory": "All Territories"
- },
+ },
{
- "doctype": "Applicable Territory",
- "parentfield": "territories",
+ "doctype": "Applicable Territory",
+ "parentfield": "territories",
"territory": "_Test Territory Rest Of The World"
}
]
- },
+ },
{
- "company": "_Test Company",
- "doctype": "Sales Taxes and Charges Master",
+ "company": "_Test Company",
+ "doctype": "Sales Taxes and Charges Master",
"taxes": [
{
- "account_head": "_Test Account Shipping Charges - _TC",
- "charge_type": "Actual",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Shipping Charges",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
- "rate": 100
- },
+ "account_head": "_Test Account Shipping Charges - _TC",
+ "charge_type": "Actual",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Shipping Charges",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
+ "tax_amount": 100
+ },
{
- "account_head": "_Test Account Customs Duty - _TC",
- "charge_type": "On Net Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Customs Duty",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
+ "account_head": "_Test Account Customs Duty - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Customs Duty",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
"rate": 10
- },
+ },
{
- "account_head": "_Test Account Excise Duty - _TC",
- "charge_type": "On Net Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Excise Duty",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
+ "account_head": "_Test Account Excise Duty - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Excise Duty",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
"rate": 12
- },
+ },
{
- "account_head": "_Test Account Education Cess - _TC",
- "charge_type": "On Previous Row Amount",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Education Cess",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
- "rate": 2,
+ "account_head": "_Test Account Education Cess - _TC",
+ "charge_type": "On Previous Row Amount",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
+ "rate": 2,
"row_id": 3
- },
+ },
{
- "account_head": "_Test Account S&H Education Cess - _TC",
- "charge_type": "On Previous Row Amount",
- "cost_center": "_Test Cost Center - _TC",
- "description": "S&H Education Cess",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
- "rate": 1,
+ "account_head": "_Test Account S&H Education Cess - _TC",
+ "charge_type": "On Previous Row Amount",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "S&H Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
+ "rate": 1,
"row_id": 3
- },
+ },
{
- "account_head": "_Test Account CST - _TC",
- "charge_type": "On Previous Row Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "CST",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
- "rate": 2,
+ "account_head": "_Test Account CST - _TC",
+ "charge_type": "On Previous Row Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "CST",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
+ "rate": 2,
"row_id": 5
- },
+ },
{
- "account_head": "_Test Account VAT - _TC",
- "charge_type": "On Net Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "VAT",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
"rate": 12.5
- },
+ },
{
- "account_head": "_Test Account Discount - _TC",
- "charge_type": "On Previous Row Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Discount",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
- "rate": -10,
+ "account_head": "_Test Account Discount - _TC",
+ "charge_type": "On Previous Row Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Discount",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
+ "rate": -10,
"row_id": 7
}
- ],
- "title": "_Test India Tax Master",
+ ],
+ "title": "_Test India Tax Master",
"territories": [
{
- "doctype": "Applicable Territory",
- "parentfield": "territories",
+ "doctype": "Applicable Territory",
+ "parentfield": "territories",
"territory": "_Test Territory India"
}
]
- },
+ },
{
- "company": "_Test Company",
- "doctype": "Sales Taxes and Charges Master",
+ "company": "_Test Company",
+ "doctype": "Sales Taxes and Charges Master",
"taxes": [
{
- "account_head": "_Test Account VAT - _TC",
- "charge_type": "On Net Total",
- "description": "VAT",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
"rate": 12
- },
+ },
{
- "account_head": "_Test Account Service Tax - _TC",
- "charge_type": "On Net Total",
- "description": "Service Tax",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "taxes",
+ "account_head": "_Test Account Service Tax - _TC",
+ "charge_type": "On Net Total",
+ "description": "Service Tax",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "taxes",
"rate": 4
}
- ],
- "title": "_Test Sales Taxes and Charges Master - Rest of the World",
+ ],
+ "title": "_Test Sales Taxes and Charges Master - Rest of the World",
"territories": [
{
- "doctype": "Applicable Territory",
- "parentfield": "territories",
+ "doctype": "Applicable Territory",
+ "parentfield": "territories",
"territory": "_Test Territory Rest Of The World"
}
]
}
-]
\ No newline at end of file
+]
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index a4be3f9..3d6ba19 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -226,13 +226,13 @@
# set precision in the last item iteration
if n == len(self.doc.get("items")) - 1:
self.round_off_totals(tax)
-
+
# adjust Discount Amount loss in last tax iteration
if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
and self.doc.discount_amount:
self.adjust_discount_amount_loss(tax)
-
-
+
+
def get_current_tax_amount(self, item, tax, item_tax_map):
tax_rate = self._get_tax_rate(tax, item_tax_map)
@@ -279,18 +279,23 @@
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount +
discount_amount_loss, tax.precision("tax_amount"))
tax.total = flt(tax.total + discount_amount_loss, tax.precision("total"))
-
+
self._set_in_company_currency(tax, ["total", "tax_amount_after_discount_amount"])
def calculate_totals(self):
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total
if self.doc.get("taxes") else self.doc.net_total)
+ self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total,
+ self.doc.precision("total_taxes_and_charges"))
+
+ self._set_in_company_currency(self.doc, ["total_taxes_and_charges"])
+
if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
if self.doc.total_taxes_and_charges else self.doc.base_net_total
else:
- self.doc.taxes_and_charges_added, self.taxes_and_charges_deducted = 0.0, 0.0
+ self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
for tax in self.doc.get("taxes"):
if tax.category in ["Valuation and Total", "Total"]:
if tax.add_deduct_tax == "Add":
@@ -306,10 +311,6 @@
self._set_in_company_currency(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
- self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total,
- self.doc.precision("total_taxes_and_charges"))
-
- self._set_in_company_currency(self.doc, ["total_taxes_and_charges"])
self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
if self.doc.meta.get_field("rounded_total"):
@@ -338,20 +339,20 @@
for i, item in enumerate(self.doc.get("items")):
distributed_amount = flt(self.doc.discount_amount) * \
item.net_amount / total_for_discount_amount
-
+
item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
net_total += item.net_amount
-
+
# discount amount rounding loss adjustment if no taxes
if (not taxes or self.doc.apply_discount_on == "Net Total") \
and i == len(self.doc.get("items")) - 1:
- discount_amount_loss = flt(self.doc.total - net_total - self.doc.discount_amount,
+ discount_amount_loss = flt(self.doc.total - net_total - self.doc.discount_amount,
self.doc.precision("net_total"))
- item.net_amount = flt(item.net_amount + discount_amount_loss,
+ item.net_amount = flt(item.net_amount + discount_amount_loss,
item.precision("net_amount"))
-
+
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
-
+
self._set_in_company_currency(item, ["net_rate", "net_amount"])
self.discount_amount_applied = True
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index de4dc37..b6909aa 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -124,6 +124,9 @@
item_fields = ("item_name", "description", "item_group", "brand")
for d in self.items:
+ if not d.item_code:
+ continue
+
item = frappe.db.get_value("Item", d.item_code, item_fields, as_dict=True)
for key in item_fields:
if not d.get(key): d.set(key, item.get(key))
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 9913054..b1e0161 100644
--- 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_add, cint
+from frappe.utils import getdate, validate_email_add, cint, today
from frappe.model.naming import make_autoname
from frappe import throw, _, msgprint
import frappe.permissions
@@ -205,6 +205,6 @@
def get_employees_who_are_born_today():
"""Get Employee properties whose birthday is today."""
return frappe.db.sql("""select name, personal_email, company_email, employee_name
- from tabEmployee where day(date_of_birth) = day(curdate())
- and month(date_of_birth) = month(curdate())
- and status = 'Active'""", as_dict=True)
+ from tabEmployee where day(date_of_birth) = day(%(date)s)
+ and month(date_of_birth) = month(%(date)s)
+ and status = 'Active'""", {"date": today()}, as_dict=True)
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
index a7d48fc..08891e0 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
@@ -1,99 +1,99 @@
{
- "allow_copy": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "creation": "2014-11-27 14:12:07.542534",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Master",
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "creation": "2014-11-27 14:12:07.542534",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Master",
"fields": [
{
- "fieldname": "capacity_planning",
- "fieldtype": "Section Break",
- "label": "Capacity Planning",
- "permlevel": 0,
+ "fieldname": "capacity_planning",
+ "fieldtype": "Section Break",
+ "label": "Capacity Planning",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "description": "Plan time logs outside Workstation Working Hours.",
- "fieldname": "allow_overtime",
- "fieldtype": "Check",
- "label": "Allow Overtime",
- "permlevel": 0,
+ "description": "Plan time logs outside Workstation Working Hours.",
+ "fieldname": "allow_overtime",
+ "fieldtype": "Check",
+ "label": "Allow Overtime",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "default": "",
- "fieldname": "allow_production_on_holidays",
- "fieldtype": "Check",
- "in_list_view": 1,
- "label": "Allow Production on Holidays",
- "options": "",
- "permlevel": 0,
+ "default": "",
+ "fieldname": "allow_production_on_holidays",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "label": "Allow Production on Holidays",
+ "options": "",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "permlevel": 0,
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "default": "30",
- "description": "Try planning operations for X days in advance.",
- "fieldname": "capacity_planning_for_days",
- "fieldtype": "Data",
- "label": "Capacity Planning For (Days)",
- "permlevel": 0,
+ "default": "30",
+ "description": "Try planning operations for X days in advance.",
+ "fieldname": "capacity_planning_for_days",
+ "fieldtype": "Data",
+ "label": "Capacity Planning For (Days)",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "description": "Default 10 mins",
- "fieldname": "mins_between_operations",
- "fieldtype": "Data",
- "label": "Time Between Operations (in mins)",
- "permlevel": 0,
+ "description": "Default 10 mins",
+ "fieldname": "mins_between_operations",
+ "fieldtype": "Data",
+ "label": "Time Between Operations (in mins)",
+ "permlevel": 0,
"precision": ""
}
- ],
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "icon-wrench",
- "in_create": 0,
- "in_dialog": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "modified": "2015-02-23 23:44:45.917027",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "Manufacturing Settings",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "icon-wrench",
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 1,
+ "istable": 0,
+ "modified": "2015-03-05 23:44:45.917027",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Manufacturing Settings",
+ "name_case": "",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 0,
- "email": 0,
- "export": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Manufacturing Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 0,
+ "export": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 0,
+ "read": 1,
+ "report": 0,
+ "role": "Manufacturing Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
"write": 1
}
- ],
- "read_only": 0,
- "read_only_onload": 0,
- "sort_field": "modified",
+ ],
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
"sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.js b/erpnext/manufacturing/doctype/production_order/production_order.js
index aee2a8a..b7fb89d 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.js
+++ b/erpnext/manufacturing/doctype/production_order/production_order.js
@@ -200,7 +200,7 @@
},
show_time_logs: function(doc, doctype, name) {
- frappe.route_options = {"operation_id": name};
+ frappe.route_options = {"operation": name};
frappe.set_route("List", "Time Log");
},
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.json b/erpnext/manufacturing/doctype/production_order/production_order.json
index bf03b2b..53f43f4 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.json
+++ b/erpnext/manufacturing/doctype/production_order/production_order.json
@@ -1,385 +1,386 @@
{
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2013-01-10 16:34:16",
- "docstatus": 0,
- "doctype": "DocType",
+ "allow_import": 1,
+ "autoname": "naming_series:",
+ "creation": "2013-01-10 16:34:16",
+ "docstatus": 0,
+ "doctype": "DocType",
"fields": [
{
- "fieldname": "item",
- "fieldtype": "Section Break",
- "label": "",
- "options": "icon-gift",
+ "fieldname": "item",
+ "fieldtype": "Section Break",
+ "label": "",
+ "options": "icon-gift",
"permlevel": 0
- },
+ },
{
- "default": "PRO-",
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "PRO-",
- "permlevel": 0,
+ "default": "PRO-",
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Series",
+ "options": "PRO-",
+ "permlevel": 0,
"reqd": 1
- },
+ },
{
- "default": "Draft",
- "depends_on": "eval:!doc.__islocal",
- "fieldname": "status",
- "fieldtype": "Select",
- "in_filter": 1,
- "in_list_view": 0,
- "label": "Status",
- "no_copy": 1,
- "oldfieldname": "status",
- "oldfieldtype": "Select",
- "options": "\nDraft\nSubmitted\nStopped\nIn Process\nCompleted\nCancelled",
- "permlevel": 0,
- "read_only": 1,
- "reqd": 1,
+ "default": "Draft",
+ "depends_on": "eval:!doc.__islocal",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_filter": 1,
+ "in_list_view": 0,
+ "label": "Status",
+ "no_copy": 1,
+ "oldfieldname": "status",
+ "oldfieldtype": "Select",
+ "options": "\nDraft\nSubmitted\nStopped\nIn Process\nCompleted\nCancelled",
+ "permlevel": 0,
+ "read_only": 1,
+ "reqd": 1,
"search_index": 1
- },
+ },
{
- "fieldname": "production_item",
- "fieldtype": "Link",
- "in_filter": 1,
- "in_list_view": 1,
- "label": "Item To Manufacture",
- "oldfieldname": "production_item",
- "oldfieldtype": "Link",
- "options": "Item",
- "permlevel": 0,
- "read_only": 0,
+ "fieldname": "production_item",
+ "fieldtype": "Link",
+ "in_filter": 1,
+ "in_list_view": 1,
+ "label": "Item To Manufacture",
+ "oldfieldname": "production_item",
+ "oldfieldtype": "Link",
+ "options": "Item",
+ "permlevel": 0,
+ "read_only": 0,
"reqd": 1
- },
+ },
{
- "depends_on": "",
- "description": "",
- "fieldname": "bom_no",
- "fieldtype": "Link",
- "in_list_view": 0,
- "label": "BOM No",
- "oldfieldname": "bom_no",
- "oldfieldtype": "Link",
- "options": "BOM",
- "permlevel": 0,
- "read_only": 0,
+ "depends_on": "",
+ "description": "",
+ "fieldname": "bom_no",
+ "fieldtype": "Link",
+ "in_list_view": 0,
+ "label": "BOM No",
+ "oldfieldname": "bom_no",
+ "oldfieldtype": "Link",
+ "options": "BOM",
+ "permlevel": 0,
+ "read_only": 0,
"reqd": 1
- },
+ },
{
- "default": "1",
- "description": "Plan material for sub-assemblies",
- "fieldname": "use_multi_level_bom",
- "fieldtype": "Check",
- "label": "Use Multi-Level BOM",
+ "default": "1",
+ "description": "Plan material for sub-assemblies",
+ "fieldname": "use_multi_level_bom",
+ "fieldtype": "Check",
+ "label": "Use Multi-Level BOM",
"permlevel": 0
- },
+ },
{
- "fieldname": "column_break1",
- "fieldtype": "Column Break",
- "oldfieldtype": "Column Break",
- "permlevel": 0,
- "read_only": 0,
+ "fieldname": "column_break1",
+ "fieldtype": "Column Break",
+ "oldfieldtype": "Column Break",
+ "permlevel": 0,
+ "read_only": 0,
"width": "50%"
- },
+ },
{
- "depends_on": "",
- "fieldname": "qty",
- "fieldtype": "Float",
- "in_list_view": 0,
- "label": "Qty To Manufacture",
- "oldfieldname": "qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "read_only": 0,
+ "depends_on": "",
+ "fieldname": "qty",
+ "fieldtype": "Float",
+ "in_list_view": 0,
+ "label": "Qty To Manufacture",
+ "oldfieldname": "qty",
+ "oldfieldtype": "Currency",
+ "permlevel": 0,
+ "read_only": 0,
"reqd": 1
- },
+ },
{
- "description": "",
- "fieldname": "material_transferred_for_qty",
- "fieldtype": "Int",
- "label": "Material Transferred for Qty",
- "permlevel": 0,
- "precision": "",
+ "description": "",
+ "fieldname": "material_transferred_for_qty",
+ "fieldtype": "Int",
+ "label": "Material Transferred for Qty",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "default": "0",
- "depends_on": "eval:doc.docstatus==1",
- "description": "",
- "fieldname": "produced_qty",
- "fieldtype": "Float",
- "label": "Manufactured Qty",
- "no_copy": 1,
- "oldfieldname": "produced_qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
+ "default": "0",
+ "depends_on": "eval:doc.docstatus==1",
+ "description": "",
+ "fieldname": "produced_qty",
+ "fieldtype": "Float",
+ "label": "Manufactured Qty",
+ "no_copy": 1,
+ "oldfieldname": "produced_qty",
+ "oldfieldtype": "Currency",
+ "permlevel": 0,
"read_only": 1
- },
+ },
{
- "fieldname": "warehouses",
- "fieldtype": "Section Break",
- "label": "Warehouses",
- "options": "icon-building",
+ "fieldname": "warehouses",
+ "fieldtype": "Section Break",
+ "label": "Warehouses",
+ "options": "icon-building",
"permlevel": 0
- },
+ },
{
- "fieldname": "wip_warehouse",
- "fieldtype": "Link",
- "label": "Work-in-Progress Warehouse",
- "options": "Warehouse",
- "permlevel": 0,
+ "fieldname": "wip_warehouse",
+ "fieldtype": "Link",
+ "label": "Work-in-Progress Warehouse",
+ "options": "Warehouse",
+ "permlevel": 0,
"reqd": 0
- },
+ },
{
- "fieldname": "column_break_12",
- "fieldtype": "Column Break",
+ "fieldname": "column_break_12",
+ "fieldtype": "Column Break",
"permlevel": 0
- },
+ },
{
- "depends_on": "",
- "description": "",
- "fieldname": "fg_warehouse",
- "fieldtype": "Link",
- "in_list_view": 0,
- "label": "Target Warehouse",
- "options": "Warehouse",
- "permlevel": 0,
- "read_only": 0,
+ "depends_on": "",
+ "description": "",
+ "fieldname": "fg_warehouse",
+ "fieldtype": "Link",
+ "in_list_view": 0,
+ "label": "Target Warehouse",
+ "options": "Warehouse",
+ "permlevel": 0,
+ "read_only": 0,
"reqd": 0
- },
+ },
{
- "fieldname": "time",
- "fieldtype": "Section Break",
- "label": "Time",
- "options": "icon-time",
- "permlevel": 0,
+ "fieldname": "time",
+ "fieldtype": "Section Break",
+ "label": "Time",
+ "options": "icon-time",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "depends_on": "",
- "fieldname": "expected_delivery_date",
- "fieldtype": "Date",
- "label": "Expected Delivery Date",
- "permlevel": 0,
+ "depends_on": "",
+ "fieldname": "expected_delivery_date",
+ "fieldtype": "Date",
+ "label": "Expected Delivery Date",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "planned_start_date",
- "fieldtype": "Datetime",
- "label": "Planned Start Date",
- "permlevel": 0,
+ "default": "now",
+ "fieldname": "planned_start_date",
+ "fieldtype": "Datetime",
+ "label": "Planned Start Date",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "planned_end_date",
- "fieldtype": "Datetime",
- "label": "Planned End Date",
- "permlevel": 0,
+ "fieldname": "planned_end_date",
+ "fieldtype": "Datetime",
+ "label": "Planned End Date",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "column_break_13",
- "fieldtype": "Column Break",
- "permlevel": 0,
+ "fieldname": "column_break_13",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "actual_start_date",
- "fieldtype": "Datetime",
- "label": "Actual Start Date",
- "permlevel": 0,
- "precision": "",
+ "fieldname": "actual_start_date",
+ "fieldtype": "Datetime",
+ "label": "Actual Start Date",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "fieldname": "actual_end_date",
- "fieldtype": "Datetime",
- "label": "Actual End Date",
- "permlevel": 0,
- "precision": "",
+ "fieldname": "actual_end_date",
+ "fieldtype": "Datetime",
+ "label": "Actual End Date",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "depends_on": "",
- "fieldname": "operations_section",
- "fieldtype": "Section Break",
- "label": "Operations",
- "options": "icon-wrench",
- "permlevel": 0,
+ "depends_on": "",
+ "fieldname": "operations_section",
+ "fieldtype": "Section Break",
+ "label": "Operations",
+ "options": "icon-wrench",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "operations",
- "fieldtype": "Table",
- "label": "Operations",
- "options": "Production Order Operation",
- "permlevel": 0,
- "precision": "",
+ "fieldname": "operations",
+ "fieldtype": "Table",
+ "label": "Operations",
+ "options": "Production Order Operation",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "fieldname": "section_break_22",
- "fieldtype": "Section Break",
- "label": "Operation Cost",
- "options": "",
- "permlevel": 0,
+ "fieldname": "section_break_22",
+ "fieldtype": "Section Break",
+ "label": "Operation Cost",
+ "options": "",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "planned_operating_cost",
- "fieldtype": "Currency",
- "label": "Planned Operating Cost",
- "no_copy": 0,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "precision": "",
+ "fieldname": "planned_operating_cost",
+ "fieldtype": "Currency",
+ "label": "Planned Operating Cost",
+ "no_copy": 0,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "fieldname": "actual_operating_cost",
- "fieldtype": "Currency",
- "label": "Actual Operating Cost",
- "no_copy": 1,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "precision": "",
+ "fieldname": "actual_operating_cost",
+ "fieldtype": "Currency",
+ "label": "Actual Operating Cost",
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "fieldname": "additional_operating_cost",
- "fieldtype": "Currency",
- "label": "Additional Operating Cost",
- "no_copy": 1,
- "options": "Company:company:default_currency",
- "permlevel": 0,
+ "fieldname": "additional_operating_cost",
+ "fieldtype": "Currency",
+ "label": "Additional Operating Cost",
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "column_break_24",
- "fieldtype": "Column Break",
- "permlevel": 0,
+ "fieldname": "column_break_24",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "total_operating_cost",
- "fieldtype": "Currency",
- "label": "Total Operating Cost",
- "no_copy": 1,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "precision": "",
+ "fieldname": "total_operating_cost",
+ "fieldtype": "Currency",
+ "label": "Total Operating Cost",
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "fieldname": "more_info",
- "fieldtype": "Section Break",
- "label": "More Info",
- "options": "icon-file-text",
- "permlevel": 0,
+ "fieldname": "more_info",
+ "fieldtype": "Section Break",
+ "label": "More Info",
+ "options": "icon-file-text",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "description",
- "fieldtype": "Small Text",
- "label": "Item Description",
- "permlevel": 0,
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "label": "Item Description",
+ "permlevel": 0,
"read_only": 1
- },
+ },
{
- "depends_on": "",
- "fieldname": "stock_uom",
- "fieldtype": "Link",
- "label": "Stock UOM",
- "oldfieldname": "stock_uom",
- "oldfieldtype": "Data",
- "options": "UOM",
- "permlevel": 0,
+ "depends_on": "",
+ "fieldname": "stock_uom",
+ "fieldtype": "Link",
+ "label": "Stock UOM",
+ "oldfieldname": "stock_uom",
+ "oldfieldtype": "Data",
+ "options": "UOM",
+ "permlevel": 0,
"read_only": 1
- },
+ },
{
- "fieldname": "column_break2",
- "fieldtype": "Column Break",
- "permlevel": 0,
- "read_only": 0,
+ "fieldname": "column_break2",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
+ "read_only": 0,
"width": "50%"
- },
+ },
{
- "fieldname": "project_name",
- "fieldtype": "Link",
- "in_filter": 1,
- "label": "Project Name",
- "oldfieldname": "project_name",
- "oldfieldtype": "Link",
- "options": "Project",
- "permlevel": 0,
+ "fieldname": "project_name",
+ "fieldtype": "Link",
+ "in_filter": 1,
+ "label": "Project Name",
+ "oldfieldname": "project_name",
+ "oldfieldtype": "Link",
+ "options": "Project",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "description": "Manufacture against Sales Order",
- "fieldname": "sales_order",
- "fieldtype": "Link",
- "label": "Sales Order",
- "options": "Sales Order",
- "permlevel": 0,
+ "description": "Manufacture against Sales Order",
+ "fieldname": "sales_order",
+ "fieldtype": "Link",
+ "label": "Sales Order",
+ "options": "Sales Order",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "oldfieldname": "company",
- "oldfieldtype": "Link",
- "options": "Company",
- "permlevel": 0,
- "read_only": 0,
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "oldfieldname": "company",
+ "oldfieldtype": "Link",
+ "options": "Company",
+ "permlevel": 0,
+ "read_only": 0,
"reqd": 1
- },
+ },
{
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Amended From",
- "no_copy": 1,
- "oldfieldname": "amended_from",
- "oldfieldtype": "Data",
- "options": "Production Order",
- "permlevel": 0,
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Amended From",
+ "no_copy": 1,
+ "oldfieldname": "amended_from",
+ "oldfieldtype": "Data",
+ "options": "Production Order",
+ "permlevel": 0,
"read_only": 1
}
- ],
- "icon": "icon-cogs",
- "idx": 1,
- "in_create": 0,
- "is_submittable": 1,
- "modified": "2015-02-26 04:03:28.164713",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "Production Order",
- "owner": "Administrator",
+ ],
+ "icon": "icon-cogs",
+ "idx": 1,
+ "in_create": 0,
+ "is_submittable": 1,
+ "modified": "2015-03-05 13:03:28.164713",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Production Order",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 1,
- "apply_user_permissions": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Manufacturing User",
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "apply_user_permissions": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Manufacturing User",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "apply_user_permissions": 1,
- "permlevel": 0,
- "read": 1,
- "report": 1,
+ "apply_user_permissions": 1,
+ "permlevel": 0,
+ "read": 1,
+ "report": 1,
"role": "Material User"
}
- ],
+ ],
"title_field": "production_item"
-}
\ No newline at end of file
+}
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py
index cb9b584..e4a7292 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/production_order.py
@@ -4,12 +4,11 @@
from __future__ import unicode_literals
import frappe, json
-from frappe.utils import flt, nowdate, get_datetime, getdate, date_diff, time_diff_in_seconds
+from frappe.utils import flt, nowdate, get_datetime, getdate, date_diff
from frappe import _
from frappe.model.document import Document
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from dateutil.relativedelta import relativedelta
-from dateutil.parser import parse
class OverProductionError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass
@@ -64,7 +63,7 @@
def calculate_operating_cost(self):
self.planned_operating_cost, self.actual_operating_cost = 0.0, 0.0
for d in self.get("operations"):
- d.actual_operating_cost = flt(d.hour_rate) * flt(d.actual_operation_time) / 60
+ d.actual_operating_cost = flt(d.hour_rate) * (flt(d.actual_operation_time) / 60.0)
self.planned_operating_cost += flt(d.planned_operating_cost)
self.actual_operating_cost += flt(d.actual_operating_cost)
@@ -273,16 +272,8 @@
def check_operation_fits_in_working_hours(self, d):
"""Raises expection if operation is longer than working hours in the given workstation."""
- operation_length = time_diff_in_seconds(d.planned_end_time, d.planned_start_time)
-
- workstation = frappe.get_doc("Workstation", d.workstation)
- for working_hour in workstation.working_hours:
- slot_length = (parse(working_hour.end_time) - parse(working_hour.start_time)).total_seconds()
- if slot_length >= operation_length:
- return
-
- frappe.throw(_("Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations").format(d.operation, d.workstation),
- OperationTooLongError)
+ from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours
+ check_if_within_operating_hours(d.workstation, d.operation, d.planned_start_time, d.planned_end_time)
def update_operation_status(self):
for d in self.get("operations"):
@@ -386,10 +377,11 @@
time_log.production_order = name
time_log.project = project
time_log.operation_id = operation_id
- time_log.operation= operation
+ time_log.operation = operation
time_log.workstation= workstation
time_log.activity_type= "Manufacturing"
time_log.completed_qty = flt(qty)
+
if from_time and to_time :
time_log.calculate_total_hours()
return time_log
diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py
index 4fb7d24..37df081 100644
--- a/erpnext/manufacturing/doctype/production_order/test_production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py
@@ -5,10 +5,11 @@
from __future__ import unicode_literals
import unittest
import frappe
+from frappe.utils import flt, get_datetime
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.manufacturing.doctype.production_order.production_order import make_stock_entry
from erpnext.stock.doctype.stock_entry import test_stock_entry
-from erpnext.projects.doctype.time_log.time_log import OverProductionError
+from erpnext.projects.doctype.time_log.time_log import OverProductionLoggedError
class TestProductionOrder(unittest.TestCase):
def check_planned_qty(self):
@@ -27,7 +28,7 @@
target="Stores - _TC", qty=100, incoming_rate=100)
# from stores to wip
- s = frappe.get_doc(make_stock_entry(pro_doc.name, "Material Transfer", 4))
+ s = frappe.get_doc(make_stock_entry(pro_doc.name, "Material Transfer for Manufacture", 4))
for d in s.get("items"):
d.s_warehouse = "Stores - _TC"
s.fiscal_year = "_Test Fiscal Year 2013"
@@ -45,6 +46,7 @@
self.assertEqual(frappe.db.get_value("Production Order", pro_doc.name,
"produced_qty"), 4)
planned1 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item", "warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty")
+
self.assertEqual(planned1 - planned0, 6)
return pro_doc
@@ -66,38 +68,34 @@
self.assertRaises(StockOverProductionError, s.submit)
def test_make_time_log(self):
+ from erpnext.manufacturing.doctype.production_order.production_order import make_time_log
+ from frappe.utils import cstr
+ from frappe.utils import time_diff_in_hours
+
prod_order = frappe.get_doc({
"doctype": "Production Order",
"production_item": "_Test FG Item 2",
"bom_no": "BOM/_Test FG Item 2/002",
"qty": 1,
"wip_warehouse": "_Test Warehouse - _TC",
- "fg_warehouse": "_Test Warehouse 1 - _TC"
+ "fg_warehouse": "_Test Warehouse 1 - _TC",
+ "company": "_Test Company",
+ "planned_start_date": "2014-11-25 00:00:00"
})
-
prod_order.set_production_order_operations()
- prod_order.operations[0].update({
- "planned_start_time": "2014-11-25 00:00:00",
- "planned_end_time": "2014-11-25 10:00:00",
- "hour_rate": 10
- })
-
prod_order.insert()
-
+ prod_order.submit()
d = prod_order.operations[0]
- from erpnext.manufacturing.doctype.production_order.production_order import make_time_log
- from frappe.utils import cstr
- from frappe.utils import time_diff_in_hours
+ d.completed_qty = flt(d.completed_qty)
- prod_order.submit()
-
- time_log = make_time_log( prod_order.name, cstr(d.idx) + ". " + d.operation, \
- d.planned_start_time, d.planned_end_time, prod_order.qty - d.qty_completed)
+ time_log = make_time_log(prod_order.name, cstr(d.idx) + ". " + d.operation, \
+ d.planned_start_time, d.planned_end_time, prod_order.qty - d.completed_qty,
+ operation_id=d.name)
self.assertEqual(prod_order.name, time_log.production_order)
- self.assertEqual((prod_order.qty - d.qty_completed), time_log.qty)
+ self.assertEqual((prod_order.qty - d.completed_qty), time_log.completed_qty)
self.assertEqual(time_diff_in_hours(d.planned_end_time, d.planned_start_time),time_log.hours)
time_log.save()
@@ -105,7 +103,6 @@
manufacturing_settings = frappe.get_doc({
"doctype": "Manufacturing Settings",
- "maximum_overtime": 30,
"allow_production_on_holidays": 0
})
@@ -113,30 +110,30 @@
prod_order.load_from_db()
self.assertEqual(prod_order.operations[0].status, "Completed")
- self.assertEqual(prod_order.operations[0].qty_completed, prod_order.qty)
+ self.assertEqual(prod_order.operations[0].completed_qty, prod_order.qty)
- self.assertEqual(prod_order.operations[0].actual_start_time, time_log.from_time)
- self.assertEqual(prod_order.operations[0].actual_end_time, time_log.to_time)
+ self.assertEqual(get_datetime(prod_order.operations[0].actual_start_time), get_datetime(time_log.from_time))
+ self.assertEqual(get_datetime(prod_order.operations[0].actual_end_time), get_datetime(time_log.to_time))
- self.assertEqual(prod_order.operations[0].actual_operation_time, 600)
- self.assertEqual(prod_order.operations[0].actual_operating_cost, 6000)
+ self.assertEqual(prod_order.operations[0].actual_operation_time, 60)
+ self.assertEqual(prod_order.operations[0].actual_operating_cost, 100)
time_log.cancel()
prod_order.load_from_db()
self.assertEqual(prod_order.operations[0].status, "Pending")
- self.assertEqual(prod_order.operations[0].qty_completed, 0)
+ self.assertEqual(flt(prod_order.operations[0].completed_qty), 0)
- self.assertEqual(prod_order.operations[0].actual_operation_time, 0)
- self.assertEqual(prod_order.operations[0].actual_operating_cost, 0)
+ self.assertEqual(flt(prod_order.operations[0].actual_operation_time), 0)
+ self.assertEqual(flt(prod_order.operations[0].actual_operating_cost), 0)
time_log2 = frappe.copy_doc(time_log)
time_log2.update({
- "qty": 10,
+ "completed_qty": 10,
"from_time": "2014-11-26 00:00:00",
"to_time": "2014-11-26 00:00:00",
"docstatus": 0
})
- self.assertRaises(OverProductionError, time_log2.save)
+ self.assertRaises(OverProductionLoggedError, time_log2.save)
test_records = frappe.get_test_records('Production Order')
diff --git a/erpnext/manufacturing/doctype/workstation/test_records.json b/erpnext/manufacturing/doctype/workstation/test_records.json
index ffc1ec8..6d54cb2 100644
--- a/erpnext/manufacturing/doctype/workstation/test_records.json
+++ b/erpnext/manufacturing/doctype/workstation/test_records.json
@@ -4,7 +4,10 @@
"name": "_Test Workstation 1",
"workstation_name": "_Test Workstation 1",
"warehouse": "_Test warehouse - _TC",
- "hour_rate":100,
+ "hour_rate_labour": 25,
+ "hour_rate_electricity": 25,
+ "hour_rate_consumable": 25,
+ "hour_rate_rent": 25,
"holiday_list": "_Test Holiday List",
"working_hours": [
{
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py
index 747acf9..d73332d 100644
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py
@@ -4,6 +4,7 @@
import frappe
import unittest
+from .workstation import check_if_within_operating_hours, NotInWorkingHoursError, WorkstationHolidayError
test_dependencies = ["Warehouse"]
test_records = frappe.get_test_records('Workstation')
@@ -11,6 +12,11 @@
class TestWorkstation(unittest.TestCase):
def test_validate_timings(self):
- wks = frappe.get_doc("Workstation", "_Test Workstation 1")
- self.assertEqual(1,wks.check_workstation_for_operation_time("2013-02-01 05:00:00", "2013-02-02 20:00:00"))
- self.assertEqual(None,wks.check_workstation_for_operation_time("2013-02-03 10:00:00", "2013-02-03 20:00:00"))
+ check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00")
+ check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00")
+ self.assertRaises(NotInWorkingHoursError, check_if_within_operating_hours,
+ "_Test Workstation 1", "Operation 1", "2013-02-02 05:00:00", "2013-02-02 20:00:00")
+ self.assertRaises(NotInWorkingHoursError, check_if_within_operating_hours,
+ "_Test Workstation 1", "Operation 1", "2013-02-02 05:00:00", "2013-02-02 20:00:00")
+ self.assertRaises(WorkstationHolidayError, check_if_within_operating_hours,
+ "_Test Workstation 1", "Operation 1", "2013-02-01 10:00:00", "2013-02-02 20:00:00")
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py
index 9ca88c5..46ac74d 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation.py
@@ -4,29 +4,21 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import flt, cint, getdate, formatdate, comma_and, get_datetime
-
+from frappe.utils import flt, cint, getdate, formatdate, comma_and, time_diff_in_seconds
from frappe.model.document import Document
+from dateutil.parser import parse
class WorkstationHolidayError(frappe.ValidationError): pass
class NotInWorkingHoursError(frappe.ValidationError): pass
class OverlapError(frappe.ValidationError): pass
class Workstation(Document):
- def update_bom_operation(self):
- bom_list = frappe.db.sql("""select DISTINCT parent from `tabBOM Operation`
- where workstation = %s""", self.name)
- for bom_no in bom_list:
- frappe.db.sql("""update `tabBOM Operation` set hour_rate = %s
- where parent = %s and workstation = %s""",
- (self.hour_rate, bom_no[0], self.name))
+ def validate(self):
+ self.hour_rate = (flt(self.hour_rate_labour) + flt(self.hour_rate_electricity) +
+ flt(self.hour_rate_consumable) + flt(self.hour_rate_rent))
def on_update(self):
self.validate_overlap_for_operation_timings()
-
- frappe.db.set(self, 'hour_rate', flt(self.hour_rate_labour) + flt(self.hour_rate_electricity) +
- flt(self.hour_rate_consumable) + flt(self.hour_rate_rent))
-
self.update_bom_operation()
def validate_overlap_for_operation_timings(self):
@@ -43,32 +35,35 @@
if existing:
frappe.throw(_("Row #{0}: Timings conflicts with row {1}").format(d.idx, comma_and(existing)), OverlapError)
+ def update_bom_operation(self):
+ bom_list = frappe.db.sql("""select DISTINCT parent from `tabBOM Operation`
+ where workstation = %s""", self.name)
+ for bom_no in bom_list:
+ frappe.db.sql("""update `tabBOM Operation` set hour_rate = %s
+ where parent = %s and workstation = %s""",
+ (self.hour_rate, bom_no[0], self.name))
+
@frappe.whitelist()
def get_default_holiday_list():
return frappe.db.get_value("Company", frappe.defaults.get_user_default("company"), "default_holiday_list")
-def check_if_within_operating_hours(workstation, from_datetime, to_datetime):
- if not cint(frappe.db.get_value("Manufacturing Settings", None, "allow_overtime")):
- is_within_operating_hours(workstation, from_datetime, to_datetime)
-
+def check_if_within_operating_hours(workstation, operation, from_datetime, to_datetime):
if not cint(frappe.db.get_value("Manufacturing Settings", "None", "allow_production_on_holidays")):
check_workstation_for_holiday(workstation, from_datetime, to_datetime)
-def is_within_operating_hours(workstation, from_datetime, to_datetime):
- start_time = get_datetime(from_datetime).time()
- end_time = get_datetime(to_datetime).time()
+ if not cint(frappe.db.get_value("Manufacturing Settings", None, "allow_overtime")):
+ is_within_operating_hours(workstation, operation, from_datetime, to_datetime)
- working_hours = frappe.db.sql_list("""select idx from `tabWorkstation Working Hour`
- where parent = %s
- and (
- (start_time between %s and %s) or
- (end_time between %s and %s) or
- (%s between start_time and end_time))
- """, (workstation, start_time, end_time, start_time, end_time, start_time))
+def is_within_operating_hours(workstation, operation, from_datetime, to_datetime):
+ operation_length = time_diff_in_seconds(to_datetime, from_datetime)
+ workstation = frappe.get_doc("Workstation", workstation)
- if not working_hours:
- frappe.throw(_("Time Log timings outside workstation operating hours"), NotInWorkingHoursError)
+ for working_hour in workstation.working_hours:
+ slot_length = (parse(working_hour.end_time) - parse(working_hour.start_time)).total_seconds()
+ if slot_length >= operation_length:
+ return
+ frappe.throw(_("Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations").format(operation, workstation.name), NotInWorkingHoursError)
def check_workstation_for_holiday(workstation, from_datetime, to_datetime):
holiday_list = frappe.db.get_value("Workstation", workstation, "holiday_list")
diff --git a/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json b/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
index a513cde..4ca5936 100644
--- a/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
+++ b/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
@@ -1,129 +1,129 @@
{
- "allow_copy": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "creation": "2014-12-24 14:46:40.678236",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "creation": "2014-12-24 14:46:40.678236",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
"fields": [
{
- "allow_on_submit": 0,
- "fieldname": "start_time",
- "fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Start Time",
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "fieldname": "start_time",
+ "fieldtype": "Time",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Start Time",
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "column_break_2",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "end_time",
- "fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "End Time",
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "fieldname": "end_time",
+ "fieldtype": "Time",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "End Time",
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "section_break_2",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "fieldname": "section_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "default": "1",
- "fieldname": "enabled",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Enabled",
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "default": "1",
+ "fieldname": "enabled",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Enabled",
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
}
- ],
- "hide_heading": 0,
- "hide_toolbar": 0,
- "in_create": 0,
- "in_dialog": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "modified": "2015-02-11 14:55:55.650726",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "Workstation Working Hour",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "read_only": 0,
- "read_only_onload": 0,
- "sort_field": "modified",
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "modified": "2015-03-05 14:55:55.650726",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Workstation Working Hour",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
"sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index 9042c83..744d6b4 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -4,4 +4,5 @@
import frappe
-test_records = frappe.get_test_records('Project')
\ No newline at end of file
+test_records = frappe.get_test_records('Project')
+test_ignore = ["Task"]
diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py
index 186fb41..6f75e0d 100644
--- a/erpnext/projects/doctype/task/test_task.py
+++ b/erpnext/projects/doctype/task/test_task.py
@@ -5,5 +5,5 @@
import frappe
test_records = frappe.get_test_records('Task')
-
-test_ignore = ["Customer"]
\ No newline at end of file
+test_dependencies = ["Project"]
+test_ignore = ["Customer"]
diff --git a/erpnext/projects/doctype/time_log/test_records.json b/erpnext/projects/doctype/time_log/test_records.json
index 0ef276e..077071c 100644
--- a/erpnext/projects/doctype/time_log/test_records.json
+++ b/erpnext/projects/doctype/time_log/test_records.json
@@ -1,10 +1,12 @@
[
{
- "activity_type": "_Test Activity Type",
- "docstatus": 1,
- "doctype": "Time Log",
- "from_time": "2013-01-01 10:00:00.000000",
- "note": "_Test Note",
- "to_time": "2013-01-01 11:00:00.000000"
+ "activity_type": "_Test Activity Type",
+ "docstatus": 1,
+ "doctype": "Time Log",
+ "from_time": "2013-01-01 10:00:00.000000",
+ "note": "_Test Note",
+ "to_time": "2013-01-01 11:00:00.000000",
+ "time_log_for": "Project",
+ "project": "_Test Project"
}
-]
\ No newline at end of file
+]
diff --git a/erpnext/projects/doctype/time_log/test_time_log.py b/erpnext/projects/doctype/time_log/test_time_log.py
index 9d4e37f..6b04132 100644
--- a/erpnext/projects/doctype/time_log/test_time_log.py
+++ b/erpnext/projects/doctype/time_log/test_time_log.py
@@ -10,17 +10,22 @@
from erpnext.projects.doctype.time_log.time_log import NotSubmittedError
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
-from erpnext.manufacturing.doctype.workstation.workstation import WorkstationIsClosedError
+from erpnext.manufacturing.doctype.workstation.workstation import NotInWorkingHoursError
from erpnext.projects.doctype.time_log_batch.test_time_log_batch import *
class TestTimeLog(unittest.TestCase):
def test_duplication(self):
frappe.db.sql("delete from `tabTime Log`")
- frappe.get_doc(frappe.copy_doc(test_records[0])).insert()
- ts = frappe.get_doc(frappe.copy_doc(test_records[0]))
- self.assertRaises(OverlapError, ts.insert)
+ tl1 = frappe.get_doc(frappe.copy_doc(test_records[0]))
+ tl1.user = "test@example.com"
+ tl1.insert()
+
+ tl2 = frappe.get_doc(frappe.copy_doc(test_records[0]))
+ tl2.user = "test@example.com"
+
+ self.assertRaises(OverlapError, tl2.insert)
frappe.db.sql("delete from `tabTime Log`")
@@ -42,7 +47,7 @@
def test_time_log_on_holiday(self):
prod_order = make_prod_order(self)
-
+ prod_order.set_production_order_operations()
prod_order.save()
prod_order.submit()
@@ -50,7 +55,10 @@
"doctype": "Time Log",
"time_log_for": "Manufacturing",
"production_order": prod_order.name,
+ "operation": prod_order.operations[0].operation,
+ "operation_id": prod_order.operations[0].name,
"qty": 1,
+ "activity_type": "_Test Activity Type",
"from_time": "2013-02-01 10:00:00",
"to_time": "2013-02-01 20:00:00",
"workstation": "_Test Workstation 1"
@@ -61,9 +69,9 @@
"from_time": "2013-02-02 09:00:00",
"to_time": "2013-02-02 20:00:00"
})
- self.assertRaises(WorkstationIsClosedError , time_log.save)
+ self.assertRaises(NotInWorkingHoursError , time_log.save)
- time_log.from_time= "2013-02-02 09:30:00"
+ time_log.from_time= "2013-02-02 10:30:00"
time_log.save()
time_log.submit()
time_log.cancel()
@@ -84,7 +92,7 @@
"bom_no": "BOM/_Test FG Item 2/002",
"qty": 1,
"wip_warehouse": "_Test Warehouse - _TC",
- "fg_warehouse": "_Test Warehouse 1 - _TC"
+ "fg_warehouse": "_Test Warehouse 1 - _TC",
})
test_records = frappe.get_test_records('Time Log')
diff --git a/erpnext/projects/doctype/time_log/time_log.json b/erpnext/projects/doctype/time_log/time_log.json
index 2316cc8..5ab2efa 100644
--- a/erpnext/projects/doctype/time_log/time_log.json
+++ b/erpnext/projects/doctype/time_log/time_log.json
@@ -1,283 +1,283 @@
{
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2013-04-03 16:38:41",
- "description": "Log of Activities performed by users against Tasks that can be used for tracking time, billing.",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Master",
+ "allow_import": 1,
+ "autoname": "naming_series:",
+ "creation": "2013-04-03 16:38:41",
+ "description": "Log of Activities performed by users against Tasks that can be used for tracking time, billing.",
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Master",
"fields": [
{
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "TL-",
- "permlevel": 0,
- "read_only": 0,
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Series",
+ "options": "TL-",
+ "permlevel": 0,
+ "read_only": 0,
"reqd": 1
- },
+ },
{
- "fieldname": "from_time",
- "fieldtype": "Datetime",
- "in_list_view": 0,
- "label": "From Time",
- "permlevel": 0,
- "read_only": 0,
+ "fieldname": "from_time",
+ "fieldtype": "Datetime",
+ "in_list_view": 0,
+ "label": "From Time",
+ "permlevel": 0,
+ "read_only": 0,
"reqd": 1
- },
+ },
{
- "fieldname": "to_time",
- "fieldtype": "Datetime",
- "in_list_view": 0,
- "label": "To Time",
- "permlevel": 0,
- "read_only": 0,
+ "fieldname": "to_time",
+ "fieldtype": "Datetime",
+ "in_list_view": 0,
+ "label": "To Time",
+ "permlevel": 0,
+ "read_only": 0,
"reqd": 1
- },
+ },
{
- "fieldname": "hours",
- "fieldtype": "Float",
- "in_list_view": 1,
- "label": "Hours",
- "permlevel": 0,
+ "fieldname": "hours",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Hours",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "billable",
- "fieldtype": "Check",
- "in_list_view": 0,
- "label": "Billable",
- "permlevel": 0,
+ "fieldname": "billable",
+ "fieldtype": "Check",
+ "in_list_view": 0,
+ "label": "Billable",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "user",
- "fieldtype": "Link",
- "label": "User",
- "options": "User",
- "permlevel": 0,
+ "fieldname": "user",
+ "fieldtype": "Link",
+ "label": "User",
+ "options": "User",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "permlevel": 0,
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 0,
- "label": "Status",
- "options": "Draft\nSubmitted\nBatched for Billing\nBilled\nCancelled",
- "permlevel": 0,
- "read_only": 1,
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 0,
+ "label": "Status",
+ "options": "Draft\nSubmitted\nBatched for Billing\nBilled\nCancelled",
+ "permlevel": 0,
+ "read_only": 1,
"reqd": 0
- },
+ },
{
- "default": "Project",
- "fieldname": "time_log_for",
- "fieldtype": "Select",
- "label": "Time Log For",
- "options": "\nProject\nManufacturing",
- "permlevel": 0,
- "precision": "",
- "read_only": 1,
+ "default": "Project",
+ "fieldname": "time_log_for",
+ "fieldtype": "Select",
+ "label": "Time Log For",
+ "options": "Project\nManufacturing",
+ "permlevel": 0,
+ "precision": "",
+ "read_only": 1,
"reqd": 0
- },
+ },
{
- "depends_on": "",
- "fieldname": "activity_type",
- "fieldtype": "Link",
- "in_list_view": 0,
- "label": "Activity Type",
- "options": "Activity Type",
- "permlevel": 0,
- "read_only": 0,
+ "depends_on": "",
+ "fieldname": "activity_type",
+ "fieldtype": "Link",
+ "in_list_view": 0,
+ "label": "Activity Type",
+ "options": "Activity Type",
+ "permlevel": 0,
+ "read_only": 0,
"reqd": 1
- },
+ },
{
- "depends_on": "eval:doc.time_log_for != 'Manufacturing'",
- "fieldname": "task",
- "fieldtype": "Link",
- "label": "Task",
- "options": "Task",
- "permlevel": 0,
+ "depends_on": "eval:doc.time_log_for != 'Manufacturing'",
+ "fieldname": "task",
+ "fieldtype": "Link",
+ "label": "Task",
+ "options": "Task",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
- "fieldname": "section_break_11",
- "fieldtype": "Section Break",
- "permlevel": 0,
+ "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
- "fieldname": "production_order",
- "fieldtype": "Link",
- "label": "Production Order",
- "options": "Production Order",
- "permlevel": 0,
- "precision": "",
+ "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
+ "fieldname": "production_order",
+ "fieldtype": "Link",
+ "label": "Production Order",
+ "options": "Production Order",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
- "fieldname": "operation",
- "fieldtype": "Select",
- "label": "Operation",
- "options": "",
- "permlevel": 0,
- "precision": "",
+ "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
+ "fieldname": "operation",
+ "fieldtype": "Select",
+ "label": "Operation",
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "fieldname": "operation_id",
- "fieldtype": "Data",
- "label": "Operation ID",
- "options": "",
- "permlevel": 0,
- "precision": "",
+ "fieldname": "operation_id",
+ "fieldtype": "Data",
+ "label": "Operation ID",
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "fieldname": "column_break_14",
- "fieldtype": "Column Break",
- "permlevel": 0,
+ "fieldname": "column_break_14",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
- "fieldname": "workstation",
- "fieldtype": "Link",
- "label": "Workstation",
- "options": "Workstation",
- "permlevel": 0,
- "precision": "",
+ "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
+ "fieldname": "workstation",
+ "fieldtype": "Link",
+ "label": "Workstation",
+ "options": "Workstation",
+ "permlevel": 0,
+ "precision": "",
"read_only": 1
- },
+ },
{
- "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
- "description": "Operation completed for how many finished goods?",
- "fieldname": "completed_qty",
- "fieldtype": "Float",
- "label": "Completed Qty",
- "permlevel": 0,
+ "depends_on": "eval:doc.time_log_for == 'Manufacturing'",
+ "description": "Operation completed for how many finished goods?",
+ "fieldname": "completed_qty",
+ "fieldtype": "Float",
+ "label": "Completed Qty",
+ "permlevel": 0,
"precision": ""
- },
+ },
{
- "fieldname": "section_break_7",
- "fieldtype": "Section Break",
- "permlevel": 0,
+ "fieldname": "section_break_7",
+ "fieldtype": "Section Break",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "note",
- "fieldtype": "Text Editor",
- "label": "Note",
- "permlevel": 0,
+ "fieldname": "note",
+ "fieldtype": "Text Editor",
+ "label": "Note",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "section_break_9",
- "fieldtype": "Section Break",
- "permlevel": 0,
+ "fieldname": "section_break_9",
+ "fieldtype": "Section Break",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "depends_on": "eval:doc.time_log_for",
- "fieldname": "project",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Project",
- "options": "Project",
- "permlevel": 0,
+ "depends_on": "eval:doc.time_log_for",
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Project",
+ "options": "Project",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "description": "Will be updated when batched.",
- "fieldname": "time_log_batch",
- "fieldtype": "Link",
- "label": "Time Log Batch",
- "options": "Time Log Batch",
- "permlevel": 0,
+ "description": "Will be updated when batched.",
+ "fieldname": "time_log_batch",
+ "fieldtype": "Link",
+ "label": "Time Log Batch",
+ "options": "Time Log Batch",
+ "permlevel": 0,
"read_only": 1
- },
+ },
{
- "description": "Will be updated when billed.",
- "fieldname": "sales_invoice",
- "fieldtype": "Link",
- "label": "Sales Invoice",
- "options": "Sales Invoice",
- "permlevel": 0,
+ "description": "Will be updated when billed.",
+ "fieldname": "sales_invoice",
+ "fieldtype": "Link",
+ "label": "Sales Invoice",
+ "options": "Sales Invoice",
+ "permlevel": 0,
"read_only": 1
- },
+ },
{
- "fieldname": "column_break_16",
- "fieldtype": "Column Break",
- "permlevel": 0,
+ "fieldname": "column_break_16",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Amended From",
- "no_copy": 1,
- "options": "Time Log",
- "permlevel": 1,
- "print_hide": 1,
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Time Log",
+ "permlevel": 1,
+ "print_hide": 1,
"read_only": 0
- },
+ },
{
- "fieldname": "title",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Title",
- "permlevel": 0,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Title",
+ "permlevel": 0,
"precision": ""
}
- ],
- "icon": "icon-time",
- "idx": 1,
- "is_submittable": 1,
- "modified": "2015-02-26 02:22:10.312376",
- "modified_by": "Administrator",
- "module": "Projects",
- "name": "Time Log",
- "owner": "Administrator",
+ ],
+ "icon": "icon-time",
+ "idx": 1,
+ "is_submittable": 1,
+ "modified": "2015-02-26 02:22:10.312376",
+ "modified_by": "Administrator",
+ "module": "Projects",
+ "name": "Time Log",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 1,
- "apply_user_permissions": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Projects User",
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "apply_user_permissions": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Projects User",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 1,
- "cancel": 1,
- "delete": 1,
- "email": 1,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Projects Manager",
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "delete": 1,
+ "email": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Projects Manager",
+ "share": 1,
+ "submit": 1,
"write": 1
}
- ],
+ ],
"title_field": "title"
-}
\ No newline at end of file
+}
diff --git a/erpnext/projects/doctype/time_log/time_log.py b/erpnext/projects/doctype/time_log/time_log.py
index 3e7d667..36aac54 100644
--- a/erpnext/projects/doctype/time_log/time_log.py
+++ b/erpnext/projects/doctype/time_log/time_log.py
@@ -9,7 +9,7 @@
from dateutil.parser import parse
class OverlapError(frappe.ValidationError): pass
-class OverProductionError(frappe.ValidationError): pass
+class OverProductionLoggedError(frappe.ValidationError): pass
class NotSubmittedError(frappe.ValidationError): pass
from frappe.model.document import Document
@@ -76,17 +76,22 @@
def get_overlap_for(self, fieldname):
if not self.get(fieldname):
return
- existing = frappe.db.sql("""select name, from_time, to_time from `tabTime Log` where `{0}`=%s and
+
+ existing = frappe.db.sql("""select name, from_time, to_time from `tabTime Log` where `{0}`=%(val)s and
(
- (from_time between %s and %s) or
- (to_time between %s and %s) or
- (%s between from_time and to_time))
- and name!=%s
- and ifnull(task, "")=%s
+ (from_time between %(from_time)s and %(to_time)s) or
+ (to_time between %(from_time)s and %(to_time)s) or
+ (%(from_time)s between from_time and to_time))
+ and name!=%(name)s
+ and ifnull(task, "")=%(task)s
and docstatus < 2""".format(fieldname),
- (self.get(fieldname), self.from_time, self.to_time, self.from_time,
- self.to_time, self.from_time, self.name or "No Name",
- cstr(self.task)), as_dict=True)
+ {
+ "val": self.get(fieldname),
+ "from_time": self.from_time,
+ "to_time": self.to_time,
+ "name": self.name or "No Name",
+ "task": cstr(self.task)
+ }, as_dict=True)
return existing[0] if existing else None
@@ -107,7 +112,7 @@
"""Checks if **Time Log** is between operating hours of the **Workstation**."""
if self.workstation:
from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours
- check_if_within_operating_hours(self.workstation, self.from_time, self.to_time)
+ check_if_within_operating_hours(self.workstation, self.operation, self.from_time, self.to_time)
def validate_production_order(self):
"""Throws 'NotSubmittedError' if **production order** is not submitted. """
@@ -194,7 +199,14 @@
if not self.operation:
frappe.throw(_("Operation is Mandatory"))
if not self.completed_qty:
- self.completed_qty=0
+ self.completed_qty = 0
+
+ production_order = frappe.get_doc("Production Order", self.production_order)
+ pending_qty = flt(production_order.qty) - flt(production_order.produced_qty)
+ if flt(self.completed_qty) > pending_qty:
+ frappe.throw(_("Completed Qty cannot be more than {0} for operation {1}").format(pending_qty, self.operation),
+ OverProductionLoggedError)
+
else:
self.production_order = None
self.operation = None
diff --git a/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py b/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py
index 44a587d..51a3240 100644
--- a/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py
+++ b/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py
@@ -34,7 +34,8 @@
time_log.update({
"from_time": "2013-01-02 10:00:00.000000",
"to_time": "2013-01-02 11:00:00.000000",
- "docstatus": 0
+ "docstatus": 0,
+ "time_log_for": "Project"
})
time_log.insert()
time_log.submit()
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 547ead6..e41f39e 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -469,7 +469,7 @@
pro_doc = frappe.get_doc("Production Order", self.production_order)
_validate_production_order(pro_doc)
pro_doc.run_method("update_status")
- if self.purpose in ("Manufacture", "Material Transfer for Manufacture"):
+ if self.purpose in "Manufacture":
pro_doc.run_method("update_production_order_qty")
self.update_planned_qty(pro_doc)
@@ -543,7 +543,7 @@
def get_items(self):
if not self.fg_completed_qty or not self.bom_no:
frappe.throw(_("BOM and Manufacturing Quantity are required"))
-
+
self.set('items', [])
self.validate_production_order()