Fixed Test Cases

This commit is contained in:
Anand Doshi 2015-03-05 19:31:23 +05:30
parent 06a6bcce17
commit ec5ec60764
27 changed files with 1005 additions and 972 deletions

View File

@ -29,6 +29,7 @@ def _make_test_records(verbose):
["_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],

View File

@ -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"
}
]
]

View File

@ -15,6 +15,7 @@ class TestPricingRule(unittest.TestCase):
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
"apply_on": "Item Code",
"item_code": "_Test Item",
"selling": 1,

View File

@ -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",

View File

@ -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",

View File

@ -254,9 +254,9 @@ class TestSalesInvoice(unittest.TestCase):
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 @@ class TestSalesInvoice(unittest.TestCase):
"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"):

View File

@ -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"
}
]
}
]
]

View File

@ -226,13 +226,13 @@ class calculate_taxes_and_totals(object):
# 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 @@ class calculate_taxes_and_totals(object):
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 @@ class calculate_taxes_and_totals(object):
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 @@ class calculate_taxes_and_totals(object):
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

View File

@ -124,6 +124,9 @@ class Opportunity(TransactionBase):
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))

View File

@ -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 send_birthday_reminders():
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)

View File

@ -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"
}
}

View File

@ -200,7 +200,7 @@ $.extend(cur_frm.cscript, {
},
show_time_logs: function(doc, doctype, name) {
frappe.route_options = {"operation_id": name};
frappe.route_options = {"operation": name};
frappe.set_route("List", "Time Log");
},

View File

@ -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"
}
}

View File

@ -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 @@ class ProductionOrder(Document):
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 @@ class ProductionOrder(Document):
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 @@ def make_time_log(name, operation, from_time, to_time, qty=None, project=None,
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

View File

@ -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 @@ class TestProductionOrder(unittest.TestCase):
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 @@ class TestProductionOrder(unittest.TestCase):
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 @@ class TestProductionOrder(unittest.TestCase):
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 @@ class TestProductionOrder(unittest.TestCase):
manufacturing_settings = frappe.get_doc({
"doctype": "Manufacturing Settings",
"maximum_overtime": 30,
"allow_production_on_holidays": 0
})
@ -113,30 +110,30 @@ class TestProductionOrder(unittest.TestCase):
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')

View File

@ -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": [
{

View File

@ -4,6 +4,7 @@ from __future__ import unicode_literals
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 @@ test_records = frappe.get_test_records('Workstation')
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")

View File

@ -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 @@ class Workstation(Document):
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")

View File

@ -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"
}
}

View File

@ -4,4 +4,5 @@ from __future__ import unicode_literals
import frappe
test_records = frappe.get_test_records('Project')
test_records = frappe.get_test_records('Project')
test_ignore = ["Task"]

View File

@ -5,5 +5,5 @@ from __future__ import unicode_literals
import frappe
test_records = frappe.get_test_records('Task')
test_ignore = ["Customer"]
test_dependencies = ["Project"]
test_ignore = ["Customer"]

View File

@ -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"
}
]
]

View File

@ -10,17 +10,22 @@ from erpnext.projects.doctype.time_log.time_log import OverlapError
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 @@ class TestTimeLog(unittest.TestCase):
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 @@ class TestTimeLog(unittest.TestCase):
"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 @@ class TestTimeLog(unittest.TestCase):
"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 @@ def make_prod_order(self):
"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')

View File

@ -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"
}
}

View File

@ -9,7 +9,7 @@ from dateutil.relativedelta import relativedelta
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 @@ class TimeLog(Document):
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 @@ class TimeLog(Document):
"""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 @@ class TimeLog(Document):
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

View File

@ -34,7 +34,8 @@ def create_time_log():
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()

View File

@ -469,7 +469,7 @@ class StockEntry(StockController):
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 @@ class StockEntry(StockController):
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()