Merge branch 'master' of github.com:webnotes/erpnext
Conflicts: accounts/doctype/purchase_invoice/purchase_invoice.py stock/doctype/purchase_receipt/purchase_receipt.py
This commit is contained in:
commit
55d9a62114
@ -341,7 +341,7 @@ class DocType(BuyingController):
|
||||
self.doc.posting_date,'Posting Date')
|
||||
|
||||
self.validate_write_off_account()
|
||||
|
||||
self.update_raw_material_cost()
|
||||
self.update_valuation_rate("entries")
|
||||
|
||||
def check_prev_docstatus(self):
|
||||
@ -518,4 +518,16 @@ class DocType(BuyingController):
|
||||
|
||||
def on_update(self):
|
||||
pass
|
||||
|
||||
def update_raw_material_cost(self):
|
||||
if self.sub_contracted_items:
|
||||
for d in self.doclist.get({"parentfield": "entries"}):
|
||||
rm_cost = webnotes.conn.sql(""" select raw_material_cost / quantity
|
||||
from `tabBOM` where item = %s and is_default = 1 and docstatus = 1
|
||||
and is_active = 1 """, (d.item_code,))
|
||||
rm_cost = rm_cost and flt(rm_cost[0][0]) or 0
|
||||
|
||||
d.conversion_factor = d.conversion_factor or webnotes.conn.get_value(
|
||||
"UOM Conversion Detail", {"parent": d.item_code, "uom": d.uom},
|
||||
"conversion_factor") or 1
|
||||
|
||||
d.rm_supp_cost = rm_cost * flt(d.qty) * flt(d.conversion_factor)
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-01-29 20:53:00",
|
||||
"creation": "2013-02-11 10:54:51",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-02-08 14:06:17",
|
||||
"modified": "2013-02-27 18:10:04",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -282,6 +282,35 @@
|
||||
"search_index": 0,
|
||||
"width": "150px"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "valuation_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Valuation Rate",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "conversion_factor",
|
||||
"fieldtype": "Float",
|
||||
"label": "Conversion Factor",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "rm_supp_cost",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Raw Materials Supplied Cost",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"doctype": "DocField",
|
||||
|
@ -348,3 +348,25 @@ class BuyingController(AccountsController):
|
||||
self._precision.tax = self.meta.get_precision_map(parentfield = \
|
||||
"purchase_tax_details")
|
||||
return self._precision
|
||||
|
||||
@property
|
||||
def sub_contracted_items(self):
|
||||
if not hasattr(self, "_sub_contracted_items"):
|
||||
item_codes = list(set(item.item_code for item in
|
||||
self.doclist.get({"parentfield": self.fname})))
|
||||
self._sub_contracted_items = [r[0] for r in webnotes.conn.sql("""select name
|
||||
from `tabItem` where name in (%s) and is_sub_contracted_item='Yes'""" % \
|
||||
(", ".join((["%s"]*len(item_codes))),), item_codes)]
|
||||
|
||||
return self._sub_contracted_items
|
||||
|
||||
@property
|
||||
def purchase_items(self):
|
||||
if not hasattr(self, "_purchase_items"):
|
||||
item_codes = list(set(item.item_code for item in
|
||||
self.doclist.get({"parentfield": self.fname})))
|
||||
self._purchase_items = [r[0] for r in webnotes.conn.sql("""select name
|
||||
from `tabItem` where name in (%s) and is_purchase_item='Yes'""" % \
|
||||
(", ".join((["%s"]*len(item_codes))),), item_codes)]
|
||||
|
||||
return self._purchase_items
|
||||
|
@ -1,8 +1,14 @@
|
||||
erpnext.updates = [
|
||||
["27th February", [
|
||||
"Time Log: Created Time Log System, with Calendar View."
|
||||
]],
|
||||
["26th February", [
|
||||
"Gross Profit: The report has been rewritten and now it is under Accounts module"
|
||||
]
|
||||
],
|
||||
]],
|
||||
["25th February", [
|
||||
"Employee Leave Balance: New Report",
|
||||
"Scripted Reports: Ability to create reports via Script",
|
||||
]],
|
||||
["21st February, 2013", [
|
||||
"Item: Warehouse-wise Re-order Level and Quantity",
|
||||
"Buying: Purchase Request renamed to Material Request"
|
||||
|
@ -18,130 +18,164 @@
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import webnotes
|
||||
import webnotes.model
|
||||
from webnotes.utils import nowdate, flt
|
||||
from accounts.utils import get_fiscal_year
|
||||
from webnotes.model.doclist import DocList
|
||||
import copy
|
||||
|
||||
company = webnotes.conn.get_default("company")
|
||||
|
||||
|
||||
def load_data():
|
||||
|
||||
# create default warehouse
|
||||
if not webnotes.conn.exists("Warehouse", "Default Warehouse"):
|
||||
webnotes.insert({"doctype": "Warehouse",
|
||||
"warehouse_name": "Default Warehouse",
|
||||
"warehouse_type": "Stores"})
|
||||
|
||||
# create UOM: Nos.
|
||||
if not webnotes.conn.exists("UOM", "Nos"):
|
||||
webnotes.insert({"doctype": "UOM", "uom_name": "Nos"})
|
||||
|
||||
from webnotes.tests import insert_test_data
|
||||
# create item groups and items
|
||||
insert_test_data("Item Group",
|
||||
sort_fn=lambda ig: (ig[0].get('parent_item_group'), ig[0].get('name')))
|
||||
insert_test_data("Item")
|
||||
|
||||
base_bom_fg = [
|
||||
{"doctype": "BOM", "item": "Android Jack D", "quantity": 1,
|
||||
"is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
{"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
"opn_description": "Development", "hour_rate": 10, "time_in_mins": 90},
|
||||
{"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
"qty": 2, "rate": 20, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
{"doctype": "BOM Item", "item_code": "Home Desktop 100", "operation_no": 1,
|
||||
"qty": 1, "rate": 300, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
{"doctype": "BOM Item", "item_code": "Nebula 7", "operation_no": 1,
|
||||
"qty": 5, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
]
|
||||
|
||||
base_bom_child = [
|
||||
{"doctype": "BOM", "item": "Nebula 7", "quantity": 5,
|
||||
"is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
{"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
"opn_description": "Development"},
|
||||
{"doctype": "BOM Item", "item_code": "Android Jack S", "operation_no": 1,
|
||||
"qty": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
]
|
||||
|
||||
base_bom_grandchild = [
|
||||
{"doctype": "BOM", "item": "Android Jack S", "quantity": 1,
|
||||
"is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
{"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
"opn_description": "Development"},
|
||||
{"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
"qty": 3, "rate": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
]
|
||||
|
||||
|
||||
class TestPurchaseReceipt(unittest.TestCase):
|
||||
def setUp(self):
|
||||
webnotes.conn.begin()
|
||||
load_data()
|
||||
|
||||
def test_bom_validation(self):
|
||||
# show throw error bacause bom no missing for sub-assembly item
|
||||
bom_fg = copy.deepcopy(base_bom_fg)
|
||||
self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
|
||||
# main item is not a manufacturing item
|
||||
bom_fg = copy.deepcopy(base_bom_fg)
|
||||
bom_fg[0]["item"] = "Home Desktop 200"
|
||||
bom_fg.pop(4)
|
||||
self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
|
||||
# operation no mentioed in material table not matching with operation table
|
||||
bom_fg = copy.deepcopy(base_bom_fg)
|
||||
bom_fg.pop(4)
|
||||
bom_fg[2]["operation_no"] = 2
|
||||
self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
|
||||
|
||||
def test_bom(self):
|
||||
gc_wrapper = webnotes.insert(DocList(base_bom_grandchild))
|
||||
gc_wrapper.submit()
|
||||
|
||||
bom_child = copy.deepcopy(base_bom_child)
|
||||
bom_child[2]["bom_no"] = gc_wrapper.doc.name
|
||||
child_wrapper = webnotes.insert(DocList(bom_child))
|
||||
child_wrapper.submit()
|
||||
|
||||
bom_fg = copy.deepcopy(base_bom_fg)
|
||||
bom_fg[4]["bom_no"] = child_wrapper.doc.name
|
||||
fg_wrapper = webnotes.insert(DocList(bom_fg))
|
||||
fg_wrapper.load_from_db()
|
||||
|
||||
self.check_bom_cost(fg_wrapper)
|
||||
|
||||
self.check_flat_bom(fg_wrapper, child_wrapper, gc_wrapper)
|
||||
|
||||
def check_bom_cost(self, fg_wrapper):
|
||||
expected_values = {
|
||||
"operating_cost": 15,
|
||||
"raw_material_cost": 640,
|
||||
"total_cost": 655
|
||||
test_records = [
|
||||
[
|
||||
{
|
||||
"doctype": "BOM",
|
||||
"item": "_Test FG Item",
|
||||
"quantity": 1.0,
|
||||
"is_active": 1,
|
||||
"is_default": 1,
|
||||
"docstatus": 1
|
||||
},
|
||||
{
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item",
|
||||
"parentfield": "bom_materials",
|
||||
"qty": 1.0,
|
||||
"rate": 5000.0,
|
||||
"amount": 5000.0,
|
||||
"stock_uom": "No."
|
||||
},
|
||||
{
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"parentfield": "bom_materials",
|
||||
"qty": 2.0,
|
||||
"rate": 1000.0,
|
||||
"amount": 2000.0,
|
||||
"stock_uom": "No."
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
for key in expected_values:
|
||||
self.assertEqual(flt(expected_values[key]), flt(fg_wrapper.doc.fields.get(key)))
|
||||
|
||||
def check_flat_bom(self, fg_wrapper, child_wrapper, gc_wrapper):
|
||||
expected_flat_bom_items = {
|
||||
("Home Desktop 300", fg_wrapper.doc.name): (2, 20),
|
||||
("Home Desktop 100", fg_wrapper.doc.name): (1, 300),
|
||||
("Home Desktop 300", gc_wrapper.doc.name): (30, 10)
|
||||
}
|
||||
|
||||
self.assertEqual(len(fg_wrapper.doclist.get({"parentfield": "flat_bom_details"})), 3)
|
||||
|
||||
for key, val in expected_flat_bom_items.items():
|
||||
flat_bom = fg_wrapper.doclist.get({"parentfield": "flat_bom_details",
|
||||
"item_code": key[0], "parent_bom": key[1]})[0]
|
||||
self.assertEqual(val, (flat_bom.qty, flat_bom.rate))
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
webnotes.conn.rollback()
|
||||
|
||||
|
||||
# import webnotes.model
|
||||
# from webnotes.utils import nowdate, flt
|
||||
# from accounts.utils import get_fiscal_year
|
||||
# from webnotes.model.doclist import DocList
|
||||
# import copy
|
||||
#
|
||||
# company = webnotes.conn.get_default("company")
|
||||
#
|
||||
#
|
||||
# def load_data():
|
||||
#
|
||||
# # create default warehouse
|
||||
# if not webnotes.conn.exists("Warehouse", "Default Warehouse"):
|
||||
# webnotes.insert({"doctype": "Warehouse",
|
||||
# "warehouse_name": "Default Warehouse",
|
||||
# "warehouse_type": "Stores"})
|
||||
#
|
||||
# # create UOM: Nos.
|
||||
# if not webnotes.conn.exists("UOM", "Nos"):
|
||||
# webnotes.insert({"doctype": "UOM", "uom_name": "Nos"})
|
||||
#
|
||||
# from webnotes.tests import insert_test_data
|
||||
# # create item groups and items
|
||||
# insert_test_data("Item Group",
|
||||
# sort_fn=lambda ig: (ig[0].get('parent_item_group'), ig[0].get('name')))
|
||||
# insert_test_data("Item")
|
||||
#
|
||||
# base_bom_fg = [
|
||||
# {"doctype": "BOM", "item": "Android Jack D", "quantity": 1,
|
||||
# "is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
# "opn_description": "Development", "hour_rate": 10, "time_in_mins": 90},
|
||||
# {"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
# "qty": 2, "rate": 20, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
# {"doctype": "BOM Item", "item_code": "Home Desktop 100", "operation_no": 1,
|
||||
# "qty": 1, "rate": 300, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
# {"doctype": "BOM Item", "item_code": "Nebula 7", "operation_no": 1,
|
||||
# "qty": 5, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
# ]
|
||||
#
|
||||
# base_bom_child = [
|
||||
# {"doctype": "BOM", "item": "Nebula 7", "quantity": 5,
|
||||
# "is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
# "opn_description": "Development"},
|
||||
# {"doctype": "BOM Item", "item_code": "Android Jack S", "operation_no": 1,
|
||||
# "qty": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
# ]
|
||||
#
|
||||
# base_bom_grandchild = [
|
||||
# {"doctype": "BOM", "item": "Android Jack S", "quantity": 1,
|
||||
# "is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
# "opn_description": "Development"},
|
||||
# {"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
# "qty": 3, "rate": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
# ]
|
||||
#
|
||||
#
|
||||
# class TestPurchaseReceipt(unittest.TestCase):
|
||||
# def setUp(self):
|
||||
# webnotes.conn.begin()
|
||||
# load_data()
|
||||
#
|
||||
# def test_bom_validation(self):
|
||||
# # show throw error bacause bom no missing for sub-assembly item
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
#
|
||||
# # main item is not a manufacturing item
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# bom_fg[0]["item"] = "Home Desktop 200"
|
||||
# bom_fg.pop(4)
|
||||
# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
#
|
||||
# # operation no mentioed in material table not matching with operation table
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# bom_fg.pop(4)
|
||||
# bom_fg[2]["operation_no"] = 2
|
||||
# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
#
|
||||
#
|
||||
# def test_bom(self):
|
||||
# gc_wrapper = webnotes.insert(DocList(base_bom_grandchild))
|
||||
# gc_wrapper.submit()
|
||||
#
|
||||
# bom_child = copy.deepcopy(base_bom_child)
|
||||
# bom_child[2]["bom_no"] = gc_wrapper.doc.name
|
||||
# child_wrapper = webnotes.insert(DocList(bom_child))
|
||||
# child_wrapper.submit()
|
||||
#
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# bom_fg[4]["bom_no"] = child_wrapper.doc.name
|
||||
# fg_wrapper = webnotes.insert(DocList(bom_fg))
|
||||
# fg_wrapper.load_from_db()
|
||||
#
|
||||
# self.check_bom_cost(fg_wrapper)
|
||||
#
|
||||
# self.check_flat_bom(fg_wrapper, child_wrapper, gc_wrapper)
|
||||
#
|
||||
# def check_bom_cost(self, fg_wrapper):
|
||||
# expected_values = {
|
||||
# "operating_cost": 15,
|
||||
# "raw_material_cost": 640,
|
||||
# "total_cost": 655
|
||||
# }
|
||||
#
|
||||
# for key in expected_values:
|
||||
# self.assertEqual(flt(expected_values[key]), flt(fg_wrapper.doc.fields.get(key)))
|
||||
#
|
||||
# def check_flat_bom(self, fg_wrapper, child_wrapper, gc_wrapper):
|
||||
# expected_flat_bom_items = {
|
||||
# ("Home Desktop 300", fg_wrapper.doc.name): (2, 20),
|
||||
# ("Home Desktop 100", fg_wrapper.doc.name): (1, 300),
|
||||
# ("Home Desktop 300", gc_wrapper.doc.name): (30, 10)
|
||||
# }
|
||||
#
|
||||
# self.assertEqual(len(fg_wrapper.doclist.get({"parentfield": "flat_bom_details"})), 3)
|
||||
#
|
||||
# for key, val in expected_flat_bom_items.items():
|
||||
# flat_bom = fg_wrapper.doclist.get({"parentfield": "flat_bom_details",
|
||||
# "item_code": key[0], "parent_bom": key[1]})[0]
|
||||
# self.assertEqual(val, (flat_bom.qty, flat_bom.rate))
|
||||
#
|
||||
#
|
||||
# def tearDown(self):
|
||||
# webnotes.conn.rollback()
|
@ -199,4 +199,5 @@ patch_list = [
|
||||
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Classic") # 2013-02-26',
|
||||
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Modern") # 2013-02-26',
|
||||
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Spartan") # 2013-02-26',
|
||||
"execute:(not webnotes.conn.exists('Role', 'Projects Manager')) and webnotes.doc({'doctype':'Role', 'role_name':'Projects Manager'}).insert()",
|
||||
]
|
@ -1,10 +0,0 @@
|
||||
[
|
||||
"BOM Replace Tool",
|
||||
"The BOM which will be replaced",
|
||||
"New BOM",
|
||||
"The new BOM after replacement",
|
||||
"Production",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM",
|
||||
"Replace",
|
||||
"Current BOM"
|
||||
]
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM \u0627\u0633\u062a\u0628\u062f\u0627\u0644 \u0623\u062f\u0627\u0629",
|
||||
"Current BOM": "BOM \u0627\u0644\u062d\u0627\u0644\u064a",
|
||||
"New BOM": "BOM \u062c\u062f\u064a\u062f\u0629",
|
||||
"Production": "\u0627\u0644\u0625\u0646\u062a\u0627\u062c",
|
||||
"Replace": "\u0627\u0633\u062a\u0628\u062f\u0644",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0627\u0633\u062a\u0628\u062f\u0627\u0644 BOM \u062e\u0627\u0635\u0629 \u0641\u064a \u062c\u0645\u064a\u0639 BOMs \u0627\u0644\u0623\u062e\u0631\u0649 \u0627\u0644\u062a\u064a \u064a\u0633\u062a\u062e\u062f\u0645 \u0641\u064a\u0647\u0627. \u0648\u0627\u0646\u0647 \u0633\u064a\u062d\u0644 \u0645\u062d\u0644 \u0627\u0644\u0631\u0627\u0628\u0637 BOM \u0627\u0644\u0642\u062f\u064a\u0645\u0629\u060c \u0648\u062a\u062d\u062f\u064a\u062b \u0648\u062a\u062c\u062f\u064a\u062f \u0627\u0644\u062a\u0643\u0644\u0641\u0629 "\u0627\u0644\u0628\u0646\u062f \u0627\u0646\u0641\u062c\u0627\u0631 BOM" \u0627\u0644\u062c\u062f\u0648\u0644 \u0627\u0644\u062c\u062f\u064a\u062f \u0648\u0641\u0642\u0627 BOM",
|
||||
"The BOM which will be replaced": "\u0648BOM \u0627\u0644\u062a\u064a \u0633\u064a\u062a\u0645 \u0627\u0633\u062a\u0628\u062f\u0627\u0644\u0647\u0627",
|
||||
"The new BOM after replacement": "\u0648BOM \u0627\u0644\u062c\u062f\u064a\u062f\u0629 \u0628\u0639\u062f \u0627\u0633\u062a\u0628\u062f\u0627\u0644"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM Tool Reemplazar",
|
||||
"Current BOM": "BOM actual",
|
||||
"New BOM": "Nueva lista de materiales",
|
||||
"Production": "Producci\u00f3n",
|
||||
"Replace": "Reemplazar",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Reemplazar un BOM particular en todas las listas de materiales de otros en los que se utiliza. Se sustituir\u00e1 el enlace BOM viejo, actualizar el costo y regenerar "Explosi\u00f3n lista de materiales Item" tabla seg\u00fan nueva lista de materiales",
|
||||
"The BOM which will be replaced": "La lista de materiales que ser\u00e1 sustituido",
|
||||
"The new BOM after replacement": "La lista de materiales nuevo despu\u00e9s de sustituirlo"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "Outil Remplacer BOM",
|
||||
"Current BOM": "Nomenclature actuelle",
|
||||
"New BOM": "Nouvelle nomenclature",
|
||||
"Production": "Production",
|
||||
"Replace": "Remplacer",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Remplacer une nomenclature particuli\u00e8re dans toutes les nomenclatures d'autres o\u00f9 il est utilis\u00e9. Il remplacera le lien de nomenclature ancienne, mettre \u00e0 jour les co\u00fbts et r\u00e9g\u00e9n\u00e9rer "Explosion de nomenclature article" la table comme pour une nouvelle nomenclature",
|
||||
"The BOM which will be replaced": "La nomenclature qui sera remplac\u00e9",
|
||||
"The new BOM after replacement": "La nouvelle nomenclature apr\u00e8s le remplacement"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "\u092c\u0940\u0913\u090f\u092e \u092c\u0926\u0932\u0947\u0902 \u0909\u092a\u0915\u0930\u0923",
|
||||
"Current BOM": "\u0935\u0930\u094d\u0924\u092e\u093e\u0928 \u092c\u0940\u0913\u090f\u092e",
|
||||
"New BOM": "\u0928\u0908 \u092c\u0940\u0913\u090f\u092e",
|
||||
"Production": "\u0909\u0924\u094d\u092a\u093e\u0926\u0928",
|
||||
"Replace": "\u092c\u0926\u0932\u0947\u0902",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0905\u0928\u094d\u092f \u0938\u092d\u0940 BOMs \u091c\u0939\u093e\u0902 \u092f\u0939 \u092a\u094d\u0930\u092f\u094b\u0917 \u0915\u093f\u092f\u093e \u091c\u093e\u0924\u093e \u0939\u0948 \u092e\u0947\u0902 \u090f\u0915 \u0935\u093f\u0936\u0947\u0937 \u092c\u0940\u0913\u090f\u092e \u092c\u0926\u0932\u0947\u0902. \u092f\u0939 \u092a\u0941\u0930\u093e\u0928\u0947 \u092c\u0940\u0913\u090f\u092e \u0932\u093f\u0902\u0915 \u0915\u0940 \u091c\u0917\u0939, \u0932\u093e\u0917\u0924 \u0905\u0926\u094d\u092f\u0924\u0928 \u0914\u0930 \u0928\u092f\u093e \u092c\u0940\u0913\u090f\u092e \u0915\u0947 \u0905\u0928\u0941\u0938\u093e\u0930 "BOM \u0927\u092e\u093e\u0915\u093e \u0906\u0907\u091f\u092e" \u0924\u093e\u0932\u093f\u0915\u093e \u092a\u0941\u0928\u0930\u094d\u091c\u0928\u094d\u092e",
|
||||
"The BOM which will be replaced": "\u092c\u0940\u0913\u090f\u092e \u091c\u094b \u092a\u094d\u0930\u0924\u093f\u0938\u094d\u0925\u093e\u092a\u093f\u0924 \u0915\u093f\u092f\u093e \u091c\u093e\u090f\u0917\u093e",
|
||||
"The new BOM after replacement": "\u092c\u0926\u0932\u0928\u0947 \u0915\u0947 \u092c\u093e\u0926 \u0928\u090f \u092c\u0940\u0913\u090f\u092e"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM Zamijenite alat",
|
||||
"Current BOM": "Trenutni BOM",
|
||||
"New BOM": "Novi BOM",
|
||||
"Production": "Proizvodnja",
|
||||
"Replace": "Zamijeniti",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Zamijenite odre\u0111eni BOM u svim drugim sastavnicama gdje se koriste. To \u0107e zamijeniti staru vezu BOM, a\u017eurirati tro\u0161kove i regenerirati "BOM eksploziju predmeta" stol kao i po novom BOM",
|
||||
"The BOM which will be replaced": "BOM koji \u0107e biti zamijenjen",
|
||||
"The new BOM after replacement": "Novi BOM nakon zamjene"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM Replace Tool",
|
||||
"Current BOM": "Actueel BOM",
|
||||
"New BOM": "Nieuwe BOM",
|
||||
"Production": "Productie",
|
||||
"Replace": "Vervang",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Vervang een bepaalde BOM in alle andere BOMs waar het wordt gebruikt. Deze vervangt de oude BOM link, updaten kosten en regenereren "BOM Explosion Item" tafel als per nieuwe BOM",
|
||||
"The BOM which will be replaced": "De BOM die zal worden vervangen",
|
||||
"The new BOM after replacement": "De nieuwe BOM na vervanging"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "Ferramenta de Substitui\u00e7\u00e3o da LDM",
|
||||
"Current BOM": "LDM atual",
|
||||
"New BOM": "Nova LDM",
|
||||
"Production": "Produ\u00e7\u00e3o",
|
||||
"Replace": "Substituir",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Substituir uma LDM espec\u00edfica em todas as LDMs outros onde ela \u00e9 usada. Isso ir\u00e1 substituir o link da LDM antiga, atualizar o custo e regenerar a tabela "Item de Explos\u00e3o da LDM" com a nova LDM",
|
||||
"The BOM which will be replaced": "A LDM que ser\u00e1 substitu\u00edda",
|
||||
"The new BOM after replacement": "A nova LDM ap\u00f3s substitui\u00e7\u00e3o"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM Ferramenta Substituir",
|
||||
"Current BOM": "BOM atual",
|
||||
"New BOM": "Novo BOM",
|
||||
"Production": "Produ\u00e7\u00e3o",
|
||||
"Replace": "Substituir",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Substituir um BOM particular em todas as BOMs outros onde ele \u00e9 usado. Ele ir\u00e1 substituir o link BOM antigo, atualizar o custo e regenerar "Explos\u00e3o BOM Item" tabela como por novo BOM",
|
||||
"The BOM which will be replaced": "O BOM que ser\u00e1 substitu\u00eddo",
|
||||
"The new BOM after replacement": "O BOM novo ap\u00f3s substitui\u00e7\u00e3o"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "\u0411\u041e\u041c \u0417\u0430\u043c\u0435\u043d\u0430 \u0430\u043b\u0430\u0442\u0430",
|
||||
"Current BOM": "\u0422\u0440\u0435\u043d\u0443\u0442\u043d\u0438 \u0411\u041e\u041c",
|
||||
"New BOM": "\u041d\u043e\u0432\u0438 \u0411\u041e\u041c",
|
||||
"Production": "\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u045a\u0430",
|
||||
"Replace": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u0438",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u043f\u043e\u0441\u0435\u0431\u043d\u0443 \u0431\u043e\u043c \u0443 \u0441\u0432\u0438\u043c \u043e\u0441\u0442\u0430\u043b\u0438\u043c \u0411\u041e\u041c\u0441 \u0433\u0434\u0435 \u0441\u0435 \u043e\u043d \u043a\u043e\u0440\u0438\u0441\u0442\u0438. \u041e\u043d\u0430 \u045b\u0435 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0443 \u0432\u0435\u0437\u0443 \u0431\u043e\u043c, \u0430\u0436\u0443\u0440\u0438\u0440\u0430\u045a\u0435 \u0442\u0440\u043e\u0448\u043a\u043e\u0432\u0435 \u0438 \u0440\u0435\u0433\u0435\u043d\u0435\u0440\u0438\u0448\u0443 "\u0431\u043e\u043c \u0435\u043a\u0441\u043f\u043b\u043e\u0437\u0438\u0458\u0435 \u0458\u0435\u0434\u0438\u043d\u0438\u0446\u0435" \u0442\u0430\u0431\u0435\u043b\u0443 \u043f\u043e \u043d\u043e\u0432\u043e\u043c \u0411\u041e\u041c",
|
||||
"The BOM which will be replaced": "\u0411\u041e\u041c \u043a\u043e\u0458\u0438 \u045b\u0435 \u0431\u0438\u0442\u0438 \u0437\u0430\u043c\u0435\u045a\u0435\u043d",
|
||||
"The new BOM after replacement": "\u041d\u043e\u0432\u0438 \u0411\u041e\u041c \u043d\u0430\u043a\u043e\u043d \u0437\u0430\u043c\u0435\u043d\u0435"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM \u0baa\u0ba4\u0bbf\u0bb2\u0bbe\u0b95 \u0b95\u0bb0\u0bc1\u0bb5\u0bbf",
|
||||
"Current BOM": "\u0ba4\u0bb1\u0bcd\u0baa\u0bc7\u0bbe\u0ba4\u0bc8\u0baf BOM",
|
||||
"New BOM": "\u0baa\u0bc1\u0ba4\u0bbf\u0baf BOM",
|
||||
"Production": "\u0b89\u0bb1\u0bcd\u0baa\u0ba4\u0bcd\u0ba4\u0bbf",
|
||||
"Replace": "\u0baa\u0ba4\u0bbf\u0bb2\u0bbe\u0b95",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0b87\u0ba4\u0bc1 \u0baa\u0baf\u0ba9\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0b95\u0bbf\u0bb1\u0ba4\u0bc1 \u0b85\u0bae\u0bc8\u0ba8\u0bcd\u0ba4\u0bc1\u0bb3\u0bcd\u0bb3 \u0b85\u0ba9\u0bc8\u0ba4\u0bcd\u0ba4\u0bc1 \u0bae\u0bb1\u0bcd\u0bb1 BOM \u0b95\u0bb3\u0bcd \u0b92\u0bb0\u0bc1 \u0b95\u0bc1\u0bb1\u0bbf\u0baa\u0bcd\u0baa\u0bbf\u0b9f\u0bcd\u0b9f BOM \u0baa\u0ba4\u0bbf\u0bb2\u0bbe\u0b95. \u0b87\u0ba4\u0bc1, \u0baa\u0bb4\u0bc8\u0baf BOM \u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0baa\u0ba4\u0bbf\u0bb2\u0bbe\u0b95 \u0b9a\u0bc6\u0bb2\u0bb5\u0bc1 \u0baa\u0bc1\u0ba4\u0bc1\u0baa\u0bcd\u0baa\u0bbf\u0b95\u0bcd\u0b95 \u0baa\u0bc1\u0ba4\u0bbf\u0baf BOM \u0baa\u0b9f\u0bbf "BOM \u0bb5\u0bc6\u0b9f\u0bbf\u0baa\u0bcd\u0baa\u0bc1 \u0baa\u0bc6\u0bbe\u0bb0\u0bc1\u0bb3\u0bcd" \u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8 \u0bae\u0bb1\u0bc1\u0b89\u0bb1\u0bcd\u0baa\u0ba4\u0bcd\u0ba4\u0bbf",
|
||||
"The BOM which will be replaced": "\u0baa\u0ba4\u0bbf\u0bb2\u0bc0\u0b9f\u0bc1 \u0b9a\u0bc6\u0baf\u0bcd\u0baf\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0bae\u0bcd BOM",
|
||||
"The new BOM after replacement": "\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1 \u0baa\u0bbf\u0ba9\u0bcd\u0ba9\u0bb0\u0bcd \u0baa\u0bc1\u0ba4\u0bbf\u0baf BOM"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07\u0e21\u0e37\u0e2d\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48 BOM",
|
||||
"Current BOM": "BOM \u0e1b\u0e31\u0e08\u0e08\u0e38\u0e1a\u0e31\u0e19",
|
||||
"New BOM": "BOM \u0e43\u0e2b\u0e21\u0e48",
|
||||
"Production": "\u0e01\u0e32\u0e23\u0e1c\u0e25\u0e34\u0e15",
|
||||
"Replace": "\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48 BOM \u0e42\u0e14\u0e22\u0e40\u0e09\u0e1e\u0e32\u0e30\u0e43\u0e19 BOMs \u0e2d\u0e37\u0e48\u0e19 \u0e46 \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14\u0e17\u0e35\u0e48\u0e16\u0e39\u0e01\u0e19\u0e33\u0e21\u0e32\u0e43\u0e0a\u0e49 \u0e21\u0e31\u0e19\u0e08\u0e30\u0e40\u0e02\u0e49\u0e32\u0e21\u0e32\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48\u0e01\u0e32\u0e23\u0e40\u0e0a\u0e37\u0e48\u0e2d\u0e21\u0e42\u0e22\u0e07 BOM \u0e40\u0e01\u0e48\u0e32\u0e1b\u0e23\u0e31\u0e1a\u0e1b\u0e23\u0e38\u0e07\u0e04\u0e48\u0e32\u0e43\u0e0a\u0e49\u0e08\u0e48\u0e32\u0e22\u0e41\u0e25\u0e30\u0e43\u0e2b\u0e49\u0e0a\u0e35\u0e27\u0e34\u0e15\u0e43\u0e2b\u0e21\u0e48 "\u0e23\u0e30\u0e40\u0e1a\u0e34\u0e14\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23 BOM" \u0e15\u0e32\u0e23\u0e32\u0e07\u0e40\u0e1b\u0e47\u0e19\u0e15\u0e48\u0e2d\u0e43\u0e2b\u0e21\u0e48 BOM",
|
||||
"The BOM which will be replaced": "BOM \u0e0b\u0e36\u0e48\u0e07\u0e08\u0e30\u0e16\u0e39\u0e01\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48",
|
||||
"The new BOM after replacement": "BOM \u0e43\u0e2b\u0e21\u0e48\u0e2b\u0e25\u0e31\u0e07\u0e08\u0e32\u0e01\u0e40\u0e1b\u0e25\u0e35\u0e48\u0e22\u0e19"
|
||||
}
|
0
projects/doctype/time_log/__init__.py
Normal file
0
projects/doctype/time_log/__init__.py
Normal file
45
projects/doctype/time_log/time_log.py
Normal file
45
projects/doctype/time_log/time_log.py
Normal file
@ -0,0 +1,45 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import webnotes
|
||||
from webnotes import _
|
||||
|
||||
from webnotes.widgets.reportview import build_match_conditions
|
||||
|
||||
class DocType:
|
||||
def __init__(self, d, dl):
|
||||
self.doc, self.doclist = d, dl
|
||||
|
||||
def validate(self):
|
||||
self.validate_overlap()
|
||||
|
||||
def validate_overlap(self):
|
||||
existing = webnotes.conn.sql_list("""select name from `tabTime Log` where owner=%s and
|
||||
((from_time between %s and %s) or (to_time between %s and %s)) and name!=%s""",
|
||||
(self.doc.owner, self.doc.from_time, self.doc.to_time, self.doc.from_time,
|
||||
self.doc.to_time, self.doc.name))
|
||||
|
||||
if existing:
|
||||
webnotes.msgprint(_("This Time Log conflicts with") + ":" + ', '.join(existing),
|
||||
raise_exception=True)
|
||||
|
||||
@webnotes.whitelist()
|
||||
def get_events(start, end):
|
||||
match = build_match_conditions("Time Log")
|
||||
data = webnotes.conn.sql("""select name, from_time, to_time,
|
||||
activity_type, task, project from `tabTime Log`
|
||||
where from_time between '%(start)s' and '%(end)s' or to_time between '%(start)s' and '%(end)s'
|
||||
%(match)s""" % {
|
||||
"start": start,
|
||||
"end": end,
|
||||
"match": match and (" and " + match) or ""
|
||||
}, as_dict=True, update={"allDay": 0})
|
||||
|
||||
for d in data:
|
||||
d.title = d.name + ": " + d.activity_type
|
||||
if d.task:
|
||||
d.title += " for Task: " + d.task
|
||||
if d.project:
|
||||
d.title += " for Project: " + d.project
|
||||
|
||||
return data
|
121
projects/doctype/time_log/time_log.txt
Normal file
121
projects/doctype/time_log/time_log.txt
Normal file
@ -0,0 +1,121 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-02-26 14:58:28",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-02-26 16:09:53",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
{
|
||||
"autoname": "TL-.######",
|
||||
"description": "Log of Activities performed by users against Tasks that can be used for tracking time, billing.",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Master",
|
||||
"module": "Projects",
|
||||
"name": "__common__"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"name": "__common__",
|
||||
"parent": "Time Log",
|
||||
"parentfield": "fields",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"name": "__common__",
|
||||
"parent": "Time Log",
|
||||
"parentfield": "permissions",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Time Log"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "from_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "From Time",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "to_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "To Time",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "activity_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Activity Type",
|
||||
"options": "Activity Type",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "task",
|
||||
"fieldtype": "Link",
|
||||
"label": "Task",
|
||||
"options": "Task"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "billable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Billable"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "section_break_7",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "note",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Note"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "section_break_9",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Project",
|
||||
"options": "Project"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "sales_invoice",
|
||||
"fieldtype": "Link",
|
||||
"label": "Sales Invoice",
|
||||
"options": "Sales Invoice"
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"match": "owner",
|
||||
"role": "Projects User"
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"role": "Projects Manager"
|
||||
}
|
||||
]
|
10
projects/doctype/time_log/time_log_calendar.js
Normal file
10
projects/doctype/time_log/time_log_calendar.js
Normal file
@ -0,0 +1,10 @@
|
||||
wn.views.calendar["Time Log"] = wn.views.Calendar.extend({
|
||||
field_map: {
|
||||
"start": "from_time",
|
||||
"end": "to_time",
|
||||
"id": "name",
|
||||
"title": "title",
|
||||
"allDay": "allDay"
|
||||
},
|
||||
get_events_method: "projects.doctype.time_log.time_log.get_events"
|
||||
})
|
@ -91,4 +91,5 @@ class DocType:
|
||||
webnotes.conn.set(self.doc, 'status', 'Submitted')
|
||||
|
||||
def on_cancel(self):
|
||||
webnotes.conn.set(self.doc, 'status', 'Cancelled')
|
||||
webnotes.conn.set(self.doc, 'status', 'Cancelled')
|
||||
|
@ -1,4 +0,0 @@
|
||||
[
|
||||
"Projects Home",
|
||||
"Projects"
|
||||
]
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u0645\u0634\u0627\u0631\u064a\u0639",
|
||||
"Projects Home": "\u0635\u0641\u062d\u0629 \u0627\u0644\u0645\u0634\u0631\u0648\u0639\u0627\u062a"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Proyectos",
|
||||
"Projects Home": "Home Proyectos"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projets",
|
||||
"Projects Home": "Accueil Projets"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u092a\u0930\u093f\u092f\u094b\u091c\u0928\u093e\u0913\u0902",
|
||||
"Projects Home": "\u092a\u0930\u093f\u092f\u094b\u091c\u0928\u093e\u0913\u0902 \u0918\u0930"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projekti",
|
||||
"Projects Home": "Projekti Po\u010detna"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projecten",
|
||||
"Projects Home": "Projecten Home"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projetos",
|
||||
"Projects Home": "In\u00edcio de Projetos"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projetos",
|
||||
"Projects Home": "Home Projetos"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u041f\u0440\u043e\u0458\u0435\u043a\u0442\u0438",
|
||||
"Projects Home": "\u041f\u0440\u043e\u0458\u0435\u043a\u0442\u0438"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u0ba4\u0bbf\u0b9f\u0bcd\u0b9f\u0b99\u0bcd\u0b95\u0bb3\u0bcd",
|
||||
"Projects Home": "\u0ba4\u0bbf\u0b9f\u0bcd\u0b9f\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0bae\u0bc1\u0b95\u0baa\u0bcd\u0baa\u0bc1"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u0e42\u0e04\u0e23\u0e07\u0e01\u0e32\u0e23",
|
||||
"Projects Home": "\u0e2b\u0e19\u0e49\u0e32\u0e41\u0e23\u0e01\u0e42\u0e04\u0e23\u0e07\u0e01\u0e32\u0e23"
|
||||
}
|
@ -21,6 +21,11 @@ wn.module_page["Projects"] = [
|
||||
description: wn._("Timesheet for tasks."),
|
||||
doctype:"Timesheet"
|
||||
},
|
||||
{
|
||||
label: wn._("Time Log"),
|
||||
description: wn._("Time Log for tasks."),
|
||||
doctype:"Time Log"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ def make_roles():
|
||||
"HR Manager", "HR User", "Leave Approver", "Maintenance Manager",
|
||||
"Maintenance User", "Manufacturing Manager", "Manufacturing User",
|
||||
"Material Manager", "Material Master Manager", "Material User",
|
||||
"Partner", "Projects User", "Purchase Manager", "Purchase Master Manager",
|
||||
"Partner", "Projects User", "Projects Manager", "Purchase Manager", "Purchase Master Manager",
|
||||
"Purchase User", "Quality Manager", "Sales Manager",
|
||||
"Sales Master Manager", "Sales User", "Supplier", "Support Manager",
|
||||
"Support Team", "Website Manager"]
|
||||
|
@ -17,64 +17,8 @@
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import webnotes
|
||||
import copy
|
||||
|
||||
from webnotes.model.bean import Bean
|
||||
from webnotes.model.doc import Document
|
||||
from webnotes.utils import flt
|
||||
|
||||
sql = webnotes.conn.sql
|
||||
|
||||
|
||||
class TestItem(unittest.TestCase):
|
||||
def setUp(self):
|
||||
webnotes.conn.begin()
|
||||
|
||||
def tearDown(self):
|
||||
webnotes.conn.rollback()
|
||||
|
||||
def testInsert(self):
|
||||
d = Bean()
|
||||
|
||||
count_before = flt(sql("select count(*) from tab"+_doctype)[0][0])
|
||||
if docok:
|
||||
for i in docok:
|
||||
d.doc = i
|
||||
d.children = None
|
||||
d.doc.fields['__islocal']=1
|
||||
d.save(1)
|
||||
count_after = flt(sql("select count(*) from tab"+_doctype)[0][0])
|
||||
self.assertTrue(count_before+len(docok)==count_after)
|
||||
|
||||
def testFailAssert(self):
|
||||
if docnotok:
|
||||
with self.assertRaises(Exception) as context:
|
||||
d = Bean()
|
||||
d.doc = docnotok[0]
|
||||
d.children = None
|
||||
d.doc.fields['__islocal']=1
|
||||
d.save(1)
|
||||
|
||||
# Test Data
|
||||
|
||||
tabOK = [
|
||||
{'is_purchase_item': None, 'is_pro_applicable': 'No', 'is_manufactured_item': None, 'description': 'Gel Ink', 'default_warehouse': None, 'item_name': 'Gel Ink', 'item_group': 'Ink', 'item_code': 'GELINK', 'is_sub_contracted_item': None, 'is_stock_item': 'Yes', 'stock_uom': 'Nos', 'docstatus': '0'},
|
||||
{'is_purchase_item': None, 'is_pro_applicable': 'No', 'is_manufactured_item': None, 'description': 'Gel Refill', 'default_warehouse': None, 'item_name': 'Gel Refill', 'item_group': 'Refill', 'item_code': 'GELREF', 'is_sub_contracted_item': None, 'is_stock_item': 'Yes', 'stock_uom': 'Nos', 'docstatus': '0'},
|
||||
{'is_purchase_item': None, 'is_pro_applicable': 'No', 'is_manufactured_item': None, 'description': 'Gel Pen', 'default_warehouse': None, 'item_name': 'Gel Pen', 'item_group': 'Pen', 'item_code': 'GELPEN', 'is_sub_contracted_item': None, 'is_stock_item': 'Yes', 'stock_uom': 'Nos', 'docstatus': '0'}
|
||||
]
|
||||
|
||||
tabNotOK = [
|
||||
{'is_purchase_item': None, 'is_pro_applicable': None, 'is_manufactured_item': None, 'description': 'F Ink', 'default_warehouse': None, 'item_name': 'F Ink', 'item_group': 'F Ink', 'item_code': None, 'is_sub_contracted_item': None, 'is_stock_item': 'No', 'stock_uom': 'Nos', 'docstatus': '0'}
|
||||
]
|
||||
|
||||
_doctype = 'Item'
|
||||
|
||||
for i in tabOK: i['doctype']=_doctype
|
||||
for i in tabNotOK: i['doctype']=_doctype
|
||||
|
||||
docok = [Document(fielddata=r) for r in tabOK]
|
||||
docnotok = [Document(fielddata=r) for r in tabNotOK]
|
||||
|
||||
test_ignore = ["BOM"]
|
||||
|
||||
test_records = [
|
||||
[{
|
||||
@ -164,4 +108,23 @@ test_records = [
|
||||
"is_sub_contracted_item": "No",
|
||||
"stock_uom": "_Test UOM"
|
||||
}],
|
||||
[{
|
||||
"doctype": "Item",
|
||||
"item_code": "_Test FG Item",
|
||||
"item_name": "_Test FG Item",
|
||||
"description": "_Test FG Item",
|
||||
"item_group": "_Test Item Group Desktops",
|
||||
"is_stock_item": "Yes",
|
||||
"is_asset_item": "No",
|
||||
"has_batch_no": "No",
|
||||
"has_serial_no": "No",
|
||||
"is_purchase_item": "Yes",
|
||||
"is_sales_item": "Yes",
|
||||
"is_service_item": "No",
|
||||
"is_sample_item": "No",
|
||||
"inspection_required": "No",
|
||||
"is_pro_applicable": "Yes",
|
||||
"is_sub_contracted_item": "Yes",
|
||||
"stock_uom": "_Test UOM"
|
||||
}],
|
||||
]
|
@ -18,10 +18,10 @@ from __future__ import unicode_literals
|
||||
import webnotes
|
||||
|
||||
from webnotes.utils import cstr, flt, cint
|
||||
from webnotes.model.doc import addchild
|
||||
from webnotes.model.bean import getlist
|
||||
from webnotes.model.code import get_obj
|
||||
from webnotes import msgprint
|
||||
from webnotes.model.doc import Document
|
||||
from webnotes import msgprint, _
|
||||
|
||||
sql = webnotes.conn.sql
|
||||
|
||||
@ -89,8 +89,6 @@ class DocType(BuyingController):
|
||||
webnotes.msgprint("Another Purchase Receipt using the same Challan No. already exists.\
|
||||
Please enter a valid Challan No.", raise_exception=1)
|
||||
|
||||
#check in manage account if purchase order required or not.
|
||||
# ====================================================================================
|
||||
def po_required(self):
|
||||
res = sql("select value from `tabSingles` where doctype = 'Global Defaults' and field = 'po_required'")
|
||||
if res and res[0][0]== 'Yes':
|
||||
@ -99,8 +97,6 @@ class DocType(BuyingController):
|
||||
msgprint("Purchse Order No. required against item %s"%d.item_code)
|
||||
raise Exception
|
||||
|
||||
|
||||
# validate
|
||||
def validate(self):
|
||||
super(DocType, self).validate()
|
||||
|
||||
@ -124,17 +120,17 @@ class DocType(BuyingController):
|
||||
pc_obj.validate_reference_value(self)
|
||||
self.check_for_stopped_status(pc_obj)
|
||||
|
||||
# sub-contracting
|
||||
self.validate_for_subcontracting()
|
||||
self.update_raw_materials_supplied()
|
||||
|
||||
self.update_valuation_rate("purchase_receipt_details")
|
||||
|
||||
|
||||
# On Update
|
||||
# ----------------------------------------------------------------------------------------------------
|
||||
def on_update(self):
|
||||
if self.doc.rejected_warehouse:
|
||||
for d in getlist(self.doclist,'purchase_receipt_details'):
|
||||
d.rejected_warehouse = self.doc.rejected_warehouse
|
||||
|
||||
self.update_rw_material_detail()
|
||||
get_obj('Stock Ledger').scrub_serial_nos(self)
|
||||
self.scrub_rejected_serial_nos()
|
||||
|
||||
@ -292,118 +288,66 @@ class DocType(BuyingController):
|
||||
|
||||
self.make_gl_entries()
|
||||
|
||||
def validate_for_subcontracting(self):
|
||||
if self.sub_contracted_items and self.purchase_items and not self.doc.is_subcontracted:
|
||||
webnotes.msgprint(_("""Please enter whether Purchase Recipt is made for subcontracting
|
||||
or purchasing, in 'Is Subcontracted' field"""), raise_exception=1)
|
||||
|
||||
if self.doc.is_subcontracted and not self.doc.supplier_warehouse:
|
||||
webnotes.msgprint(_("Please Enter Supplier Warehouse for subcontracted Items"),
|
||||
raise_exception=1)
|
||||
|
||||
def update_raw_materials_supplied(self):
|
||||
self.doclist = self.doc.clear_table(self.doclist, 'pr_raw_material_details')
|
||||
if self.sub_contracted_items:
|
||||
for item in self.doclist.get({"parentfield": "purchase_receipt_details"}):
|
||||
if item.item_code in self.sub_contracted_items:
|
||||
self.add_bom_items(item)
|
||||
|
||||
def update_rw_material_detail(self):
|
||||
def add_bom_items(self, d):
|
||||
bom_items = self.get_items_from_default_bom(d.item_code)
|
||||
raw_materials_cost = 0
|
||||
for item in bom_items:
|
||||
required_qty = flt(item.qty_consumed_per_unit) * flt(d.qty) * flt(d.conversion_factor)
|
||||
self.doclist.append({
|
||||
"parentfield": "pr_raw_material_details",
|
||||
"doctype": "Purchase Receipt Item Supplied",
|
||||
"reference_name": d.name,
|
||||
"bom_detail_no": item.name,
|
||||
"main_item_code": d.item_code,
|
||||
"rm_item_code": item.item_code,
|
||||
"description": item.description,
|
||||
"stock_uom": item.stock_uom,
|
||||
"required_qty": required_qty,
|
||||
"consumed_qty": required_qty,
|
||||
"conversion_factor": d.conversion_factor,
|
||||
"rate": item.rate,
|
||||
"amount": required_qty * flt(item.rate)
|
||||
})
|
||||
|
||||
raw_materials_cost += required_qty * flt(item.rate)
|
||||
|
||||
d.rm_supp_cost = raw_materials_cost
|
||||
|
||||
for d in getlist(self.doclist,'purchase_receipt_details'):
|
||||
item_det = sql("select is_sub_contracted_item, is_purchase_item from `tabItem` where name = '%s'"%(d.item_code))
|
||||
|
||||
if item_det[0][0] == 'Yes':
|
||||
if item_det[0][1] == 'Yes':
|
||||
if not self.doc.is_subcontracted:
|
||||
msgprint("Please enter whether purchase receipt to be made for subcontracting or for purchase in 'Is Subcontracted' field .")
|
||||
raise Exception
|
||||
if self.doc.is_subcontracted == 'Yes':
|
||||
if not self.doc.supplier_warehouse:
|
||||
msgprint("Please Enter Supplier Warehouse for subcontracted Items")
|
||||
raise Exception
|
||||
self.add_bom(d)
|
||||
else:
|
||||
self.doclist = self.doc.clear_table(self.doclist,'pr_raw_material_details',1)
|
||||
self.doc.save()
|
||||
elif item_det[0][1] == 'No':
|
||||
if not self.doc.supplier_warehouse:
|
||||
msgprint("Please Enter Supplier Warehouse for subcontracted Items")
|
||||
raise Exception
|
||||
self.add_bom(d)
|
||||
|
||||
self.delete_irrelevant_raw_material()
|
||||
#---------------calculate amt in Purchase Receipt Item Supplied-------------
|
||||
self.calculate_amount(d)
|
||||
|
||||
|
||||
def add_bom(self, d):
|
||||
#----- fetching default bom from Bill of Materials instead of Item Master --
|
||||
bom_det = sql("""select t1.item, t2.item_code, t2.qty_consumed_per_unit,
|
||||
t2.moving_avg_rate, t2.value_as_per_mar, t2.stock_uom, t2.name, t2.description
|
||||
def get_items_from_default_bom(self, item_code):
|
||||
# print webnotes.conn.sql("""select name from `tabBOM` where item = '_Test FG Item'""")
|
||||
bom_items = sql("""select t2.item_code, t2.qty_consumed_per_unit,
|
||||
t2.rate, t2.stock_uom, t2.name, t2.description
|
||||
from `tabBOM` t1, `tabBOM Item` t2
|
||||
where t2.parent = t1.name and t1.item = %s and t1.is_default = 1
|
||||
and t1.docstatus = 1 and t2.docstatus =1 and t1.is_active = 1""", d.item_code)
|
||||
if not bom_det:
|
||||
msgprint("No default BOM exists for item: %s" % d.item_code)
|
||||
raise Exception
|
||||
else:
|
||||
#-------------- add child function--------------------
|
||||
chgd_rqd_qty = []
|
||||
for i in bom_det:
|
||||
and t1.docstatus = 1 and t1.is_active = 1""", item_code, as_dict=1)
|
||||
if not bom_items:
|
||||
msgprint(_("No default BOM exists for item: ") + item_code, raise_exception=1)
|
||||
|
||||
return bom_items
|
||||
|
||||
if i and not sql("select name from `tabPurchase Receipt Item Supplied` where reference_name = '%s' and bom_detail_no = '%s' and parent = '%s' " %(d.name, i[6], self.doc.name)):
|
||||
|
||||
rm_child = addchild(self.doc, 'pr_raw_material_details', 'Purchase Receipt Item Supplied', self.doclist)
|
||||
|
||||
rm_child.reference_name = d.name
|
||||
rm_child.bom_detail_no = i and i[6] or ''
|
||||
rm_child.main_item_code = i and i[0] or ''
|
||||
rm_child.rm_item_code = i and i[1] or ''
|
||||
rm_child.description = i and i[7] or ''
|
||||
rm_child.stock_uom = i and i[5] or ''
|
||||
rm_child.rate = i and flt(i[3]) or flt(i[4])
|
||||
rm_child.conversion_factor = d.conversion_factor
|
||||
rm_child.required_qty = flt(i and flt(i[2]) or 0) * flt(d.qty) * flt(d.conversion_factor)
|
||||
rm_child.consumed_qty = flt(i and flt(i[2]) or 0) * flt(d.qty) * flt(d.conversion_factor)
|
||||
rm_child.amount = flt(flt(rm_child.consumed_qty)*flt(rm_child.rate))
|
||||
rm_child.save()
|
||||
chgd_rqd_qty.append(cstr(i[1]))
|
||||
else:
|
||||
act_qty = flt(i and flt(i[2]) or 0) * flt(d.qty) * flt(d.conversion_factor)
|
||||
for pr_rmd in getlist(self.doclist, 'pr_raw_material_details'):
|
||||
if i and i[6] == pr_rmd.bom_detail_no and (flt(act_qty) != flt(pr_rmd.required_qty) or i[1] != pr_rmd.rm_item_code or i[7] != pr_rmd.description):
|
||||
chgd_rqd_qty.append(cstr(i[1]))
|
||||
pr_rmd.main_item_code = i[0]
|
||||
pr_rmd.rm_item_code = i[1]
|
||||
pr_rmd.description = i[7]
|
||||
pr_rmd.stock_uom = i[5]
|
||||
pr_rmd.required_qty = flt(act_qty)
|
||||
pr_rmd.consumed_qty = flt(act_qty)
|
||||
pr_rmd.rate = i and flt(i[3]) or flt(i[4])
|
||||
pr_rmd.amount = flt(flt(pr_rmd.consumed_qty)*flt(pr_rmd.rate))
|
||||
pr_rmd.save()
|
||||
if chgd_rqd_qty:
|
||||
msgprint("Please check consumed quantity for Raw Material Item Code: '%s'in Raw materials Detail Table" % ((len(chgd_rqd_qty) > 1 and ','.join(chgd_rqd_qty[:-1]) +' and ' + cstr(chgd_rqd_qty[-1:][0]) ) or cstr(chgd_rqd_qty[0])))
|
||||
|
||||
|
||||
# Delete irrelevant raw material from PR Raw material details
|
||||
#--------------------------------------------------------------
|
||||
def delete_irrelevant_raw_material(self):
|
||||
for d in getlist(self.doclist,'pr_raw_material_details'):
|
||||
if not sql("select name from `tabPurchase Receipt Item` where name = '%s' and parent = '%s' and item_code = '%s'" % (d.reference_name, self.doc.name, d.main_item_code)):
|
||||
d.parent = 'old_par:'+self.doc.name
|
||||
d.save()
|
||||
|
||||
def calculate_amount(self, d):
|
||||
amt = 0
|
||||
for i in getlist(self.doclist,'pr_raw_material_details'):
|
||||
|
||||
if(i.reference_name == d.name):
|
||||
#if i.consumed_qty == 0:
|
||||
# msgprint("consumed qty cannot be 0. Please Enter consumed qty ")
|
||||
#raise Exception
|
||||
i.amount = flt(i.consumed_qty)* flt(i.rate)
|
||||
amt += i.amount
|
||||
d.rm_supp_cost = amt
|
||||
d.save()
|
||||
|
||||
|
||||
# --------------- Back Flush function called on submit and on cancel from update stock
|
||||
def bk_flush_supp_wh(self, is_submit):
|
||||
for d in getlist(self.doclist, 'pr_raw_material_details'):
|
||||
#--------- -ve quantity is passed as raw material qty has to be decreased when PR is submitted and it has to be increased when PR is cancelled
|
||||
# negative quantity is passed as raw material qty has to be decreased
|
||||
# when PR is submitted and it has to be increased when PR is cancelled
|
||||
consumed_qty = - flt(d.consumed_qty)
|
||||
self.make_sl_entry(d, self.doc.supplier_warehouse, flt(consumed_qty), 0, is_submit)
|
||||
|
||||
|
||||
# get current_stock
|
||||
# ----------------
|
||||
def get_current_stock(self):
|
||||
for d in getlist(self.doclist, 'pr_raw_material_details'):
|
||||
if self.doc.supplier_warehouse:
|
||||
|
@ -62,6 +62,17 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
self.assertEquals(expected_values[i][2], gle.credit)
|
||||
|
||||
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
|
||||
|
||||
def test_subcontracting(self):
|
||||
pr = webnotes.bean(copy=test_records[1])
|
||||
pr.run_method("calculate_taxes_and_totals")
|
||||
pr.insert()
|
||||
|
||||
self.assertEquals(pr.doclist[1].rm_supp_cost, 70000.0)
|
||||
self.assertEquals(len(pr.doclist.get({"parentfield": "pr_raw_material_details"})), 2)
|
||||
|
||||
|
||||
test_dependencies = ["BOM"]
|
||||
|
||||
test_records = [
|
||||
[
|
||||
@ -127,4 +138,36 @@ test_records = [
|
||||
"tax_amount": 150.0,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"conversion_rate": 1.0,
|
||||
"currency": "INR",
|
||||
"doctype": "Purchase Receipt",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"posting_date": "2013-02-12",
|
||||
"posting_time": "15:33:30",
|
||||
"is_subcontracted": "Yes",
|
||||
"supplier_warehouse": "_Test Warehouse",
|
||||
"supplier": "_Test Supplier",
|
||||
"net_total": 5000.0,
|
||||
"grand_total": 5000.0,
|
||||
},
|
||||
{
|
||||
"conversion_factor": 1.0,
|
||||
"description": "_Test FG Item",
|
||||
"doctype": "Purchase Receipt Item",
|
||||
"item_code": "_Test FG Item",
|
||||
"item_name": "_Test FG Item",
|
||||
"parentfield": "purchase_receipt_details",
|
||||
"received_qty": 10.0,
|
||||
"qty": 10.0,
|
||||
"rejected_qty": 0.0,
|
||||
"import_rate": 500.0,
|
||||
"amount": 5000.0,
|
||||
"warehouse": "_Test Warehouse",
|
||||
"stock_uom": "Nos",
|
||||
"uom": "_Test UOM",
|
||||
}
|
||||
],
|
||||
]
|
@ -253,6 +253,9 @@ def get_outer_env(page_name, args):
|
||||
if k in settings.fields:
|
||||
args[k] = settings.fields.get(k)
|
||||
|
||||
for k in ["facebook_share", "google_plus_one", "twitter_share", "linked_in_share"]:
|
||||
args[k] = int(args.get(k) or 0)
|
||||
|
||||
if not args.brand_html:
|
||||
args.brand_html = "ERPNext"
|
||||
if not args.top_bar_background:
|
||||
|
Loading…
Reference in New Issue
Block a user