Merge branch 'develop' of github.com:frappe/erpnext into call-summary-dialog

This commit is contained in:
Suraj Shetty 2019-06-03 12:36:45 +05:30
commit d1668e22d1
138 changed files with 453531 additions and 426425 deletions

View File

@ -1,20 +0,0 @@
include MANIFEST.in
include requirements.txt
include *.json
include *.md
include *.py
include *.txt
include .travis.yml
recursive-include erpnext *.txt
recursive-include erpnext *.css
recursive-include erpnext *.csv
recursive-include erpnext *.html
recursive-include erpnext *.ico
recursive-include erpnext *.js
recursive-include erpnext *.json
recursive-include erpnext *.md
recursive-include erpnext *.png
recursive-include erpnext *.py
recursive-include erpnext *.svg
recursive-include erpnext/public *
recursive-exclude * *.pyc

View File

@ -321,7 +321,7 @@ def set_discount_amount(rate, item_details):
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None): def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
from erpnext.accounts.doctype.pricing_rule.utils import get_apply_on_and_items from erpnext.accounts.doctype.pricing_rule.utils import get_apply_on_and_items
for d in pricing_rules.split(','): for d in pricing_rules.split(','):
if not d: continue if not d or not frappe.db.exists("Pricing Rule", d): continue
pricing_rule = frappe.get_doc('Pricing Rule', d) pricing_rule = frappe.get_doc('Pricing Rule', d)
if pricing_rule.price_or_product_discount == 'Price': if pricing_rule.price_or_product_discount == 'Price':

View File

@ -480,10 +480,10 @@ def apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate=False):
rule_applied = {} rule_applied = {}
for item in doc.get("items"): for item in doc.get("items"):
if item.get(apply_on) in items:
if not item.pricing_rules: if not item.pricing_rules:
item.pricing_rules = item_row.pricing_rules item.pricing_rules = item_row.pricing_rules
if item.get(apply_on) in items:
for field in ['discount_percentage', 'discount_amount', 'rate']: for field in ['discount_percentage', 'discount_amount', 'rate']:
if not pr_doc.get(field): continue if not pr_doc.get(field): continue

View File

@ -314,13 +314,11 @@ class Subscription(Document):
self.save() self.save()
@property
def is_postpaid_to_invoice(self): def is_postpaid_to_invoice(self):
return getdate(nowdate()) > getdate(self.current_invoice_end) or \ return getdate(nowdate()) > getdate(self.current_invoice_end) or \
(getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) and \ (getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) and \
not self.has_outstanding_invoice() not self.has_outstanding_invoice()
@property
def is_prepaid_to_invoice(self): def is_prepaid_to_invoice(self):
if not self.generate_invoice_at_period_start: if not self.generate_invoice_at_period_start:
return False return False
@ -340,7 +338,7 @@ class Subscription(Document):
2. Change the `Subscription` status to 'Past Due Date' 2. Change the `Subscription` status to 'Past Due Date'
3. Change the `Subscription` status to 'Cancelled' 3. Change the `Subscription` status to 'Cancelled'
""" """
if self.is_postpaid_to_invoice or self.is_prepaid_to_invoice: if self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice():
self.generate_invoice() self.generate_invoice()
if self.current_invoice_is_past_due(): if self.current_invoice_is_past_due():
self.status = 'Past Due Date' self.status = 'Past Due Date'

View File

@ -68,13 +68,13 @@ class ReceivablePayableReport(object):
if self.filters.based_on_payment_terms: if self.filters.based_on_payment_terms:
columns.append({ columns.append({
"label": "Payment Term", "label": _("Payment Term"),
"fieldname": "payment_term", "fieldname": "payment_term",
"fieldtype": "Data", "fieldtype": "Data",
"width": 120 "width": 120
}) })
columns.append({ columns.append({
"label": "Invoice Grand Total", "label": _("Invoice Grand Total"),
"fieldname": "invoice_grand_total", "fieldname": "invoice_grand_total",
"fieldtype": "Currency", "fieldtype": "Currency",
"options": "currency", "options": "currency",
@ -83,7 +83,7 @@ class ReceivablePayableReport(object):
for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"): for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"):
columns.append({ columns.append({
"label": label, "label": _(label),
"fieldname": frappe.scrub(label), "fieldname": frappe.scrub(label),
"fieldtype": "Currency", "fieldtype": "Currency",
"options": "currency", "options": "currency",

View File

@ -333,6 +333,9 @@ def reconcile_against_document(args):
doc = frappe.get_doc(d.voucher_type, d.voucher_no) doc = frappe.get_doc(d.voucher_type, d.voucher_no)
doc.make_gl_entries(cancel = 0, adv_adj =1) doc.make_gl_entries(cancel = 0, adv_adj =1)
if d.voucher_type in ('Payment Entry', 'Journal Entry'):
doc.update_expense_claim()
def check_if_advance_entry_modified(args): def check_if_advance_entry_modified(args):
""" """
check if there is already a voucher reference check if there is already a voucher reference
@ -375,9 +378,9 @@ def check_if_advance_entry_modified(args):
def validate_allocated_amount(args): def validate_allocated_amount(args):
if args.get("allocated_amount") < 0: if args.get("allocated_amount") < 0:
throw(_("Allocated amount can not be negative")) throw(_("Allocated amount cannot be negative"))
elif args.get("allocated_amount") > args.get("unadjusted_amount"): elif args.get("allocated_amount") > args.get("unadjusted_amount"):
throw(_("Allocated amount can not greater than unadjusted amount")) throw(_("Allocated amount cannot be greater than unadjusted amount"))
def update_reference_in_journal_entry(d, jv_obj): def update_reference_in_journal_entry(d, jv_obj):
""" """

View File

@ -33,6 +33,14 @@ frappe.ui.form.on("Purchase Order", {
} }
} }
}); });
frm.set_query("expense_account", "items", function() {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: {'company': frm.doc.company}
}
});
}, },
refresh: function(frm) { refresh: function(frm) {

View File

@ -8,7 +8,9 @@ import frappe.defaults
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from frappe.utils import flt, add_days, nowdate, getdate from frappe.utils import flt, add_days, nowdate, getdate
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_rm_stock_entry as make_subcontract_transfer_entry) from erpnext.buying.doctype.purchase_order.purchase_order \
import (make_purchase_receipt, make_purchase_invoice as make_pi_from_po, make_rm_stock_entry as make_subcontract_transfer_entry)
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_pi_from_pr
from erpnext.stock.doctype.material_request.test_material_request import make_material_request from erpnext.stock.doctype.material_request.test_material_request import make_material_request
from erpnext.stock.doctype.material_request.material_request import make_purchase_order from erpnext.stock.doctype.material_request.material_request import make_purchase_order
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
@ -62,7 +64,7 @@ class TestPurchaseOrder(unittest.TestCase):
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50) frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
pi = make_purchase_invoice(po.name) pi = make_pi_from_po(po.name)
pi.update_stock = 1 pi.update_stock = 1
pi.items[0].qty = 12 pi.items[0].qty = 12
pi.insert() pi.insert()
@ -89,7 +91,7 @@ class TestPurchaseOrder(unittest.TestCase):
create_pr_against_po(po.name) create_pr_against_po(po.name)
make_purchase_invoice(po.name) make_pi_from_po(po.name)
existing_ordered_qty = get_ordered_qty() existing_ordered_qty = get_ordered_qty()
existing_requested_qty = get_requested_qty() existing_requested_qty = get_requested_qty()
@ -111,29 +113,37 @@ class TestPurchaseOrder(unittest.TestCase):
def test_update_qty(self): def test_update_qty(self):
po = create_purchase_order() po = create_purchase_order()
make_pr_against_po(po.name, 6) pr = make_pr_against_po(po.name, 2)
po.load_from_db() po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 6) self.assertEqual(po.get("items")[0].received_qty, 2)
# Check received_qty after make_purchase_invoice without update_stock checked # Check received_qty after making PI from PR without update_stock checked
pi1 = make_purchase_invoice(po.name) pi1 = make_pi_from_pr(pr.name)
pi1.get("items")[0].qty = 6 pi1.get("items")[0].qty = 2
pi1.insert() pi1.insert()
pi1.submit() pi1.submit()
po.load_from_db() po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 6) self.assertEqual(po.get("items")[0].received_qty, 2)
# Check received_qty after make_purchase_invoice with update_stock checked # Check received_qty after making PI from PO with update_stock checked
pi2 = make_purchase_invoice(po.name) pi2 = make_pi_from_po(po.name)
pi2.set("update_stock", 1) pi2.set("update_stock", 1)
pi2.get("items")[0].qty = 3 pi2.get("items")[0].qty = 3
pi2.insert() pi2.insert()
pi2.submit() pi2.submit()
po.load_from_db() po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 9) self.assertEqual(po.get("items")[0].received_qty, 5)
# Check received_qty after making PR from PO
pr = make_pr_against_po(po.name, 1)
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 6)
def test_return_against_purchase_order(self): def test_return_against_purchase_order(self):
po = create_purchase_order() po = create_purchase_order()
@ -143,7 +153,7 @@ class TestPurchaseOrder(unittest.TestCase):
po.load_from_db() po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 6) self.assertEqual(po.get("items")[0].received_qty, 6)
pi2 = make_purchase_invoice(po.name) pi2 = make_pi_from_po(po.name)
pi2.set("update_stock", 1) pi2.set("update_stock", 1)
pi2.get("items")[0].qty = 3 pi2.get("items")[0].qty = 3
pi2.insert() pi2.insert()
@ -175,10 +185,10 @@ class TestPurchaseOrder(unittest.TestCase):
def test_make_purchase_invoice(self): def test_make_purchase_invoice(self):
po = create_purchase_order(do_not_submit=True) po = create_purchase_order(do_not_submit=True)
self.assertRaises(frappe.ValidationError, make_purchase_invoice, po.name) self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
po.submit() po.submit()
pi = make_purchase_invoice(po.name) pi = make_pi_from_po(po.name)
self.assertEqual(pi.doctype, "Purchase Invoice") self.assertEqual(pi.doctype, "Purchase Invoice")
self.assertEqual(len(pi.get("items", [])), 1) self.assertEqual(len(pi.get("items", [])), 1)
@ -186,7 +196,7 @@ class TestPurchaseOrder(unittest.TestCase):
def test_purchase_order_on_hold(self): def test_purchase_order_on_hold(self):
po = create_purchase_order(item_code="_Test Product Bundle Item") po = create_purchase_order(item_code="_Test Product Bundle Item")
po.db_set('Status', "On Hold") po.db_set('Status', "On Hold")
pi = make_purchase_invoice(po.name) pi = make_pi_from_po(po.name)
pr = make_purchase_receipt(po.name) pr = make_purchase_receipt(po.name)
self.assertRaises(frappe.ValidationError, pr.submit) self.assertRaises(frappe.ValidationError, pr.submit)
self.assertRaises(frappe.ValidationError, pi.submit) self.assertRaises(frappe.ValidationError, pi.submit)
@ -195,7 +205,7 @@ class TestPurchaseOrder(unittest.TestCase):
def test_make_purchase_invoice_with_terms(self): def test_make_purchase_invoice_with_terms(self):
po = create_purchase_order(do_not_save=True) po = create_purchase_order(do_not_save=True)
self.assertRaises(frappe.ValidationError, make_purchase_invoice, po.name) self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
po.update( po.update(
{"payment_terms_template": "_Test Payment Term Template"} {"payment_terms_template": "_Test Payment Term Template"}
@ -208,7 +218,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEqual(getdate(po.payment_schedule[0].due_date), getdate(po.transaction_date)) self.assertEqual(getdate(po.payment_schedule[0].due_date), getdate(po.transaction_date))
self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0) self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0)
self.assertEqual(getdate(po.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30)) self.assertEqual(getdate(po.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
pi = make_purchase_invoice(po.name) pi = make_pi_from_po(po.name)
pi.save() pi.save()
self.assertEqual(pi.doctype, "Purchase Invoice") self.assertEqual(pi.doctype, "Purchase Invoice")
@ -346,7 +356,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertTrue(po.get('payment_schedule')) self.assertTrue(po.get('payment_schedule'))
pi = make_purchase_invoice(po.name) pi = make_pi_from_po(po.name)
self.assertFalse(pi.get('payment_schedule')) self.assertFalse(pi.get('payment_schedule'))
@ -357,7 +367,7 @@ class TestPurchaseOrder(unittest.TestCase):
po.submit() po.submit()
self.assertTrue(po.get('payment_schedule')) self.assertTrue(po.get('payment_schedule'))
pi = make_purchase_invoice(po.name) pi = make_pi_from_po(po.name)
pi.insert() pi.insert()
self.assertTrue(pi.get('payment_schedule')) self.assertTrue(pi.get('payment_schedule'))
@ -442,7 +452,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) self.assertEquals(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# Make Purchase Invoice # Make Purchase Invoice
pi = make_purchase_invoice(po.name) pi = make_pi_from_po(po.name)
pi.update_stock = 1 pi.update_stock = 1
pi.supplier_warehouse = "_Test Warehouse 1 - _TC" pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
pi.insert() pi.insert()

View File

@ -59,18 +59,38 @@ frappe.ui.form.on("Request for Quotation",{
var dialog = new frappe.ui.Dialog({ var dialog = new frappe.ui.Dialog({
title: __("Get Suppliers"), title: __("Get Suppliers"),
fields: [ fields: [
{ "fieldtype": "Select", "label": __("Get Suppliers By"), {
"fieldtype": "Select", "label": __("Get Suppliers By"),
"fieldname": "search_type", "fieldname": "search_type",
"options": "Tag\nSupplier Group", "reqd": 1 }, "options": ["Tag","Supplier Group"],
{ "fieldtype": "Link", "label": __("Supplier Group"), "reqd": 1,
onchange() {
if(dialog.get_value('search_type') == 'Tag'){
frappe.call({
method: 'erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_supplier_tag',
}).then(r => {
dialog.set_df_property("tag", "options", r.message)
});
}
}
},
{
"fieldtype": "Link", "label": __("Supplier Group"),
"fieldname": "supplier_group", "fieldname": "supplier_group",
"options": "Supplier Group", "reqd": 0, "options": "Supplier Group",
"depends_on": "eval:doc.search_type == 'Supplier Group'"}, "reqd": 0,
{ "fieldtype": "Data", "label": __("Tag"), "depends_on": "eval:doc.search_type == 'Supplier Group'"
"fieldname": "tag", "reqd": 0, },
"depends_on": "eval:doc.search_type == 'Tag'" }, {
{ "fieldtype": "Button", "label": __("Add All Suppliers"), "fieldtype": "Select", "label": __("Tag"),
"fieldname": "add_suppliers", "cssClass": "btn-primary"}, "fieldname": "tag",
"reqd": 0,
"depends_on": "eval:doc.search_type == 'Tag'",
},
{
"fieldtype": "Button", "label": __("Add All Suppliers"),
"fieldname": "add_suppliers"
},
] ]
}); });

View File

@ -341,3 +341,16 @@ def get_item_from_material_requests_based_on_supplier(source_name, target_doc =
}, target_doc) }, target_doc)
return target_doc return target_doc
@frappe.whitelist()
def get_supplier_tag():
data = frappe.db.sql("select _user_tags from `tabSupplier`")
tags = []
for tag in data:
tags += filter(bool, tag[0].split(","))
tags = list(set(tags))
return tags

View File

@ -60,6 +60,13 @@ def get_data():
"description": _("Import Data from CSV / Excel files."), "description": _("Import Data from CSV / Excel files."),
"onboard": 1, "onboard": 1,
}, },
{
"type": "doctype",
"name": "Chart of Accounts Importer",
"labe": _("Chart Of Accounts Importer"),
"description": _("Import Chart Of Accounts from CSV / Excel files"),
"onboard": 1
},
{ {
"type": "doctype", "type": "doctype",
"name": "Letter Head", "name": "Letter Head",

View File

@ -355,7 +355,7 @@ class AccountsController(TransactionBase):
'fiscal_year': fiscal_year, 'fiscal_year': fiscal_year,
'voucher_type': self.doctype, 'voucher_type': self.doctype,
'voucher_no': self.name, 'voucher_no': self.name,
'remarks': self.get("remarks"), 'remarks': self.get("remarks") or self.get("remark"),
'debit': 0, 'debit': 0,
'credit': 0, 'credit': 0,
'debit_in_account_currency': 0, 'debit_in_account_currency': 0,

View File

@ -279,14 +279,14 @@ def copy_attributes_to_variant(item, variant):
variant.set(field.fieldname, item.get(field.fieldname)) variant.set(field.fieldname, item.get(field.fieldname))
variant.variant_of = item.name variant.variant_of = item.name
if 'description' in allow_fields:
variant.has_variants = 0 if 'description' not in allow_fields:
if not variant.description: if not variant.description:
variant.description = "" variant.description = ""
if item.variant_based_on=='Item Attribute': if item.variant_based_on=='Item Attribute':
if variant.attributes: if variant.attributes:
attributes_description = "" attributes_description = item.description + " "
for d in variant.attributes: for d in variant.attributes:
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>" attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"

View File

@ -316,19 +316,27 @@ class StatusUpdater(Document):
.format(frappe.db.escape(frappe.session.user)) .format(frappe.db.escape(frappe.session.user))
def update_billing_status_for_zero_amount_refdoc(self, ref_dt): def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
ref_fieldname = ref_dt.lower().replace(" ", "_") ref_fieldname = frappe.scrub(ref_dt)
zero_amount_refdoc = []
all_zero_amount_refdoc = frappe.db.sql_list("""select name from `tab%s`
where docstatus=1 and base_net_total = 0""" % ref_dt)
for item in self.get("items"): ref_docs = [item.get(ref_fieldname) for item in (self.get('items') or []) if item.get(ref_fieldname)]
if item.get(ref_fieldname) \ if not ref_docs:
and item.get(ref_fieldname) in all_zero_amount_refdoc \ return
and item.get(ref_fieldname) not in zero_amount_refdoc:
zero_amount_refdoc.append(item.get(ref_fieldname))
if zero_amount_refdoc: zero_amount_refdocs = frappe.db.sql_list("""
self.update_billing_status(zero_amount_refdoc, ref_dt, ref_fieldname) SELECT
name
from
`tab{ref_dt}`
where
docstatus = 1
and base_net_total = 0
and name in %(ref_docs)s
""".format(ref_dt=ref_dt), {
'ref_docs': ref_docs
})
if zero_amount_refdocs:
self.update_billing_status(zero_amount_refdocs, ref_dt, ref_fieldname)
def update_billing_status(self, zero_amount_refdoc, ref_dt, ref_fieldname): def update_billing_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
for ref_dn in zero_amount_refdoc: for ref_dn in zero_amount_refdoc:

View File

@ -80,7 +80,7 @@ class StockController(AccountsController):
"cost_center": item_row.cost_center, "cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock", "remarks": self.get("remarks") or "Accounting Entry for Stock",
"debit": flt(sle.stock_value_difference, 2), "debit": flt(sle.stock_value_difference, 2),
"is_opening": item_row.get("is_opening"), "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
}, warehouse_account[sle.warehouse]["account_currency"])) }, warehouse_account[sle.warehouse]["account_currency"]))
# to target warehouse / expense account # to target warehouse / expense account
@ -91,7 +91,7 @@ class StockController(AccountsController):
"remarks": self.get("remarks") or "Accounting Entry for Stock", "remarks": self.get("remarks") or "Accounting Entry for Stock",
"credit": flt(sle.stock_value_difference, 2), "credit": flt(sle.stock_value_difference, 2),
"project": item_row.get("project") or self.get("project"), "project": item_row.get("project") or self.get("project"),
"is_opening": item_row.get("is_opening") "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
})) }))
elif sle.warehouse not in warehouse_with_no_account: elif sle.warehouse not in warehouse_with_no_account:
warehouse_with_no_account.append(sle.warehouse) warehouse_with_no_account.append(sle.warehouse)

View File

@ -19,6 +19,7 @@ frappe.ui.form.on("Student Group", {
'academic_term': frm.doc.academic_term, 'academic_term': frm.doc.academic_term,
'program': frm.doc.program, 'program': frm.doc.program,
'batch': frm.doc.batch, 'batch': frm.doc.batch,
'student_category': frm.doc.student_category,
'course': frm.doc.course, 'course': frm.doc.course,
'student_group': frm.doc.name 'student_group': frm.doc.name
} }
@ -92,6 +93,7 @@ frappe.ui.form.on("Student Group", {
"group_based_on": frm.doc.group_based_on, "group_based_on": frm.doc.group_based_on,
"program": frm.doc.program, "program": frm.doc.program,
"batch" : frm.doc.batch, "batch" : frm.doc.batch,
"student_category" : frm.doc.student_category,
"course": frm.doc.course "course": frm.doc.course
}, },
callback: function(r) { callback: function(r) {

View File

@ -1,552 +1,161 @@
{ {
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "field:student_group_name", "autoname": "field:student_group_name",
"beta": 0,
"creation": "2015-09-07 12:55:52.072792", "creation": "2015-09-07 12:55:52.072792",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Document", "document_type": "Document",
"editable_grid": 0,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [
"academic_year",
"group_based_on",
"student_group_name",
"max_strength",
"column_break_3",
"academic_term",
"program",
"batch",
"student_category",
"course",
"disabled",
"section_break_6",
"get_students",
"students",
"section_break_12",
"instructors"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "academic_year", "fieldname": "academic_year",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Academic Year", "label": "Academic Year",
"length": 0,
"no_copy": 0,
"options": "Academic Year", "options": "Academic Year",
"permlevel": 0, "set_only_once": 1
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 1,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "group_based_on", "fieldname": "group_based_on",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Group Based on", "label": "Group Based on",
"length": 0,
"no_copy": 0,
"options": "\nBatch\nCourse\nActivity", "options": "\nBatch\nCourse\nActivity",
"permlevel": 0, "reqd": 1
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "student_group_name", "fieldname": "student_group_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Student Group Name", "label": "Student Group Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "unique": 1
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Set 0 for no limit", "description": "Set 0 for no limit",
"fieldname": "max_strength", "fieldname": "max_strength",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 0, "label": "Max Strength"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Max Strength",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3", "fieldname": "column_break_3",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "academic_term", "fieldname": "academic_term",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Academic Term", "label": "Academic Term",
"length": 0, "options": "Academic Term"
"no_copy": 0,
"options": "Academic Term",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "program", "fieldname": "program",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1, "in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Program", "label": "Program",
"length": 0, "options": "Program"
"no_copy": 0,
"options": "Program",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "batch", "fieldname": "batch",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1, "in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Batch", "label": "Batch",
"length": 0, "options": "Student Batch Name"
"no_copy": 0,
"options": "Student Batch Name",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.group_based_on == 'Course'", "depends_on": "eval:doc.group_based_on == 'Course'",
"fieldname": "course", "fieldname": "course",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1, "in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Course", "label": "Course",
"length": 0, "options": "Course"
"no_copy": 0,
"options": "Course",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0", "default": "0",
"fieldname": "disabled", "fieldname": "disabled",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "label": "Disabled"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disabled",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": "",
"columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fieldname": "section_break_6", "fieldname": "section_break_6",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Students"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Students",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "get_students", "fieldname": "get_students",
"fieldtype": "Button", "fieldtype": "Button",
"hidden": 0, "label": "Get Students"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Get Students",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "students", "fieldname": "students",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Students", "label": "Students",
"length": 0, "options": "Student Group Student"
"no_copy": 0,
"options": "Student Group Student",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_12", "fieldname": "section_break_12",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Instructors"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Instructors",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "instructors", "fieldname": "instructors",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Instructors", "label": "Instructors",
"length": 0, "options": "Student Group Instructor"
"no_copy": 0, },
"options": "Student Group Instructor", {
"permlevel": 0, "fieldname": "student_category",
"precision": "", "fieldtype": "Link",
"print_hide": 0, "label": "Student Category",
"print_hide_if_no_value": 0, "options": "Student Category"
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "modified": "2019-04-26 10:52:57.303951",
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-07-26 04:17:10.836912",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Education", "module": "Education",
"name": "Student Group", "name": "Student Group",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1, "read": 1,
"report": 0, "role": "Instructor"
"role": "Instructor",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
}, },
{ {
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Academics User", "role": "Academics User",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Education", "restrict_to_domain": "Education",
"search_fields": "program, batch, course", "search_fields": "program, batch, course",
"show_name_in_global_search": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC"
"title_field": "",
"track_changes": 0,
"track_seen": 0
} }

View File

@ -30,7 +30,7 @@ class StudentGroup(Document):
frappe.throw(_("""Cannot enroll more than {0} students for this student group.""").format(self.max_strength)) frappe.throw(_("""Cannot enroll more than {0} students for this student group.""").format(self.max_strength))
def validate_students(self): def validate_students(self):
program_enrollment = get_program_enrollment(self.academic_year, self.academic_term, self.program, self.batch, self.course) program_enrollment = get_program_enrollment(self.academic_year, self.academic_term, self.program, self.batch, self.student_category, self.course)
students = [d.student for d in program_enrollment] if program_enrollment else [] students = [d.student for d in program_enrollment] if program_enrollment else []
for d in self.students: for d in self.students:
if not frappe.db.get_value("Student", d.student, "enabled") and d.active and not self.disabled: if not frappe.db.get_value("Student", d.student, "enabled") and d.active and not self.disabled:
@ -60,8 +60,8 @@ class StudentGroup(Document):
roll_no_list.append(d.group_roll_number) roll_no_list.append(d.group_roll_number)
@frappe.whitelist() @frappe.whitelist()
def get_students(academic_year, group_based_on, academic_term=None, program=None, batch=None, course=None): def get_students(academic_year, group_based_on, academic_term=None, program=None, batch=None, student_category=None, course=None):
enrolled_students = get_program_enrollment(academic_year, academic_term, program, batch, course) enrolled_students = get_program_enrollment(academic_year, academic_term, program, batch, student_category, course)
if enrolled_students: if enrolled_students:
student_list = [] student_list = []
@ -76,7 +76,7 @@ def get_students(academic_year, group_based_on, academic_term=None, program=None
frappe.msgprint(_("No students found")) frappe.msgprint(_("No students found"))
return [] return []
def get_program_enrollment(academic_year, academic_term=None, program=None, batch=None, course=None): def get_program_enrollment(academic_year, academic_term=None, program=None, batch=None, student_category=None, course=None):
condition1 = " " condition1 = " "
condition2 = " " condition2 = " "
@ -86,6 +86,8 @@ def get_program_enrollment(academic_year, academic_term=None, program=None, batc
condition1 += " and pe.program = %(program)s" condition1 += " and pe.program = %(program)s"
if batch: if batch:
condition1 += " and pe.student_batch_name = %(batch)s" condition1 += " and pe.student_batch_name = %(batch)s"
if student_category:
condition1 += " and pe.student_category = %(student_category)s"
if course: if course:
condition1 += " and pe.name = pec.parent and pec.course = %(course)s" condition1 += " and pe.name = pec.parent and pec.course = %(course)s"
condition2 = ", `tabProgram Enrollment Course` pec" condition2 = ", `tabProgram Enrollment Course` pec"
@ -100,14 +102,14 @@ def get_program_enrollment(academic_year, academic_term=None, program=None, batc
order by order by
pe.student_name asc pe.student_name asc
'''.format(condition1=condition1, condition2=condition2), '''.format(condition1=condition1, condition2=condition2),
({"academic_year": academic_year, "academic_term":academic_term, "program": program, "batch": batch, "course": course}), as_dict=1) ({"academic_year": academic_year, "academic_term":academic_term, "program": program, "batch": batch, "student_category": student_category, "course": course}), as_dict=1)
@frappe.whitelist() @frappe.whitelist()
def fetch_students(doctype, txt, searchfield, start, page_len, filters): def fetch_students(doctype, txt, searchfield, start, page_len, filters):
if filters.get("group_based_on") != "Activity": if filters.get("group_based_on") != "Activity":
enrolled_students = get_program_enrollment(filters.get('academic_year'), filters.get('academic_term'), enrolled_students = get_program_enrollment(filters.get('academic_year'), filters.get('academic_term'),
filters.get('program'), filters.get('batch')) filters.get('program'), filters.get('batch'), filters.get('student_category'))
student_group_student = frappe.db.sql_list('''select student from `tabStudent Group Student` where parent=%s''', student_group_student = frappe.db.sql_list('''select student from `tabStudent Group Student` where parent=%s''',
(filters.get('student_group'))) (filters.get('student_group')))
students = ([d.student for d in enrolled_students if d.student not in student_group_student] students = ([d.student for d in enrolled_students if d.student not in student_group_student]

View File

@ -107,10 +107,18 @@ def get_series():
def setup_custom_fields(): def setup_custom_fields():
custom_fields = { custom_fields = {
"Customer": [dict(fieldname='shopify_customer_id', label='Shopify Customer Id', "Customer": [
fieldtype='Data', insert_after='series', read_only=1, print_hide=1)], dict(fieldname='shopify_customer_id', label='Shopify Customer Id',
"Address": [dict(fieldname='shopify_address_id', label='Shopify Address Id', fieldtype='Data', insert_after='series', read_only=1, print_hide=1)
fieldtype='Data', insert_after='fax', read_only=1, print_hide=1)], ],
"Supplier": [
dict(fieldname='shopify_supplier_id', label='Shopify Supplier Id',
fieldtype='Data', insert_after='supplier_name', read_only=1, print_hide=1)
],
"Address": [
dict(fieldname='shopify_address_id', label='Shopify Address Id',
fieldtype='Data', insert_after='fax', read_only=1, print_hide=1)
],
"Item": [ "Item": [
dict(fieldname='shopify_variant_id', label='Shopify Variant Id', dict(fieldname='shopify_variant_id', label='Shopify Variant Id',
fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1), fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1),
@ -119,16 +127,20 @@ def setup_custom_fields():
dict(fieldname='shopify_description', label='Shopify Description', dict(fieldname='shopify_description', label='Shopify Description',
fieldtype='Text Editor', insert_after='description', read_only=1, print_hide=1) fieldtype='Text Editor', insert_after='description', read_only=1, print_hide=1)
], ],
"Sales Order": [dict(fieldname='shopify_order_id', label='Shopify Order Id', "Sales Order": [
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)], dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
],
"Delivery Note":[ "Delivery Note":[
dict(fieldname='shopify_order_id', label='Shopify Order Id', dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1), fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id', dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1) fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
], ],
"Sales Invoice": [dict(fieldname='shopify_order_id', label='Shopify Order Id', "Sales Invoice": [
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)] dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
]
} }
create_custom_fields(custom_fields) create_custom_fields(custom_fields)

View File

@ -9,11 +9,13 @@ frappe.ui.form.on('Appraisal', {
return{ query: "erpnext.controllers.queries.employee_query" } return{ query: "erpnext.controllers.queries.employee_query" }
}; };
}, },
onload: function(frm) { onload: function(frm) {
if(!frm.doc.status) { if(!frm.doc.status) {
frm.set_value('status', 'Draft'); frm.set_value('status', 'Draft');
} }
}, },
kra_template: function(frm) { kra_template: function(frm) {
frm.doc.goals = []; frm.doc.goals = [];
erpnext.utils.map_current_doc({ erpnext.utils.map_current_doc({
@ -22,6 +24,7 @@ frappe.ui.form.on('Appraisal', {
frm: frm frm: frm
}); });
}, },
calculate_total: function(frm) { calculate_total: function(frm) {
let goals = frm.doc.goals || []; let goals = frm.doc.goals || [];
let total =0; let total =0;
@ -35,20 +38,17 @@ frappe.ui.form.on('Appraisal', {
frappe.ui.form.on('Appraisal Goal', { frappe.ui.form.on('Appraisal Goal', {
score: function(frm, cdt, cdn) { score: function(frm, cdt, cdn) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
if (d.score){ if (d.score) {
if (flt(d.score) > 5) { if (flt(d.score) > 5) {
frappe.msgprint(__("Score must be less than or equal to 5")); frappe.msgprint(__("Score must be less than or equal to 5"));
d.score = 0; d.score = 0;
refresh_field('score', d.name, 'goals'); refresh_field('score', d.name, 'goals');
} }
var total = flt(d.per_weightage*d.score)/100; d.score_earned = flt(d.per_weightage*d.score, precision("score_earned", d))/100;
d.score_earned = total.toPrecision(2); } else {
refresh_field('score_earned', d.name, 'goals');
}
else{
d.score_earned = 0; d.score_earned = 0;
refresh_field('score_earned', d.name, 'goals');
} }
refresh_field('score_earned', d.name, 'goals');
frm.trigger('calculate_total'); frm.trigger('calculate_total');
} }
}); });

View File

@ -219,7 +219,8 @@ frappe.ui.form.on("Expense Claim", {
frm.fields_dict["cost_center"].get_query = function() { frm.fields_dict["cost_center"].get_query = function() {
return { return {
filters: { filters: {
"company": frm.doc.company "company": frm.doc.company,
"is_group": 0
} }
}; };
}; };
@ -230,7 +231,9 @@ frappe.ui.form.on("Expense Claim", {
return { return {
filters: { filters: {
"report_type": "Balance Sheet", "report_type": "Balance Sheet",
"account_type": "Payable" "account_type": "Payable",
"company": frm.doc.company,
"is_group": 0
} }
}; };
}; };

View File

@ -0,0 +1,20 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'reference_name',
'internal_links': {
'Employee Advance': ['advances', 'employee_advance']
},
'transactions': [
{
'label': _('Payment'),
'items': ['Payment Entry', 'Journal Entry']
},
{
'label': _('Reference'),
'items': ['Employee Advance']
},
]
}

View File

@ -79,6 +79,7 @@ def get_events(start, end, filters=None):
filters.append(['Holiday', 'holiday_date', '>', getdate(start)]) filters.append(['Holiday', 'holiday_date', '>', getdate(start)])
if end: if end:
filters.append(['Holiday', 'holiday_date', '<', getdate(end)]) filters.append(['Holiday', 'holiday_date', '<', getdate(end)])
return frappe.get_list('Holiday List', return frappe.get_list('Holiday List',
fields=['name', '`tabHoliday`.holiday_date', '`tabHoliday`.description', '`tabHoliday List`.color'], fields=['name', '`tabHoliday`.holiday_date', '`tabHoliday`.description', '`tabHoliday List`.color'],
filters = filters, filters = filters,

View File

@ -3,8 +3,8 @@
frappe.views.calendar["Holiday List"] = { frappe.views.calendar["Holiday List"] = {
field_map: { field_map: {
"start": "from_date", "start": "holiday_date",
"end": "to_date", "end": "holiday_date",
"id": "name", "id": "name",
"title": "description", "title": "description",
"allDay": "allDay" "allDay": "allDay"

View File

@ -119,7 +119,7 @@ class SalarySlip(TransactionBase):
if not self.salary_slip_based_on_timesheet: if not self.salary_slip_based_on_timesheet:
self.get_date_details() self.get_date_details()
self.validate_dates() self.validate_dates()
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
["date_of_joining", "relieving_date"]) ["date_of_joining", "relieving_date"])
self.get_leave_details(joining_date, relieving_date) self.get_leave_details(joining_date, relieving_date)
@ -183,7 +183,7 @@ class SalarySlip(TransactionBase):
def get_leave_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0): def get_leave_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0):
if not joining_date: if not joining_date:
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
["date_of_joining", "relieving_date"]) ["date_of_joining", "relieving_date"])
working_days = date_diff(self.end_date, self.start_date) + 1 working_days = date_diff(self.end_date, self.start_date) + 1
@ -300,9 +300,6 @@ class SalarySlip(TransactionBase):
self.rounded_total = rounded(self.net_pay) self.rounded_total = rounded(self.net_pay)
if self.net_pay < 0:
frappe.throw(_("Net Pay cannnot be negative"))
def calculate_component_amounts(self): def calculate_component_amounts(self):
if not getattr(self, '_salary_structure_doc', None): if not getattr(self, '_salary_structure_doc', None):
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure) self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
@ -313,6 +310,7 @@ class SalarySlip(TransactionBase):
self.add_employee_benefits(payroll_period) self.add_employee_benefits(payroll_period)
self.add_additional_salary_components() self.add_additional_salary_components()
self.add_tax_components(payroll_period) self.add_tax_components(payroll_period)
self.set_component_amounts_based_on_payment_days()
def add_structure_components(self): def add_structure_components(self):
data = self.get_data_for_eval() data = self.get_data_for_eval()
@ -404,14 +402,18 @@ class SalarySlip(TransactionBase):
def add_tax_components(self, payroll_period): def add_tax_components(self, payroll_period):
# Calculate variable_based_on_taxable_salary after all components updated in salary slip # Calculate variable_based_on_taxable_salary after all components updated in salary slip
struct_tax_components = [d.salary_component for d in self._salary_structure_doc.get("deductions") tax_components, other_deduction_components = [], []
if d.variable_based_on_taxable_salary == 1 and not d.formula and not d.amount] for d in self._salary_structure_doc.get("deductions"):
if d.variable_based_on_taxable_salary == 1 and not d.formula and not flt(d.amount):
tax_components.append(d.salary_component)
else:
other_deduction_components.append(d.salary_component)
if not struct_tax_components: if not tax_components:
struct_tax_components = [d.name for d in tax_components = [d.name for d in frappe.get_all("Salary Component", filters={"variable_based_on_taxable_salary": 1})
frappe.get_all("Salary Component", filters={"variable_based_on_taxable_salary": 1})] if d.name not in other_deduction_components]
for d in struct_tax_components: for d in tax_components:
tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period) tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period)
tax_row = self.get_salary_slip_row(d) tax_row = self.get_salary_slip_row(d)
self.update_component_row(tax_row, tax_amount, "deductions") self.update_component_row(tax_row, tax_amount, "deductions")
@ -477,8 +479,7 @@ class SalarySlip(TransactionBase):
future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * (math.ceil(remaining_sub_periods) - 1) future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * (math.ceil(remaining_sub_periods) - 1)
# get taxable_earnings, addition_earnings for current actual payment days # get taxable_earnings, addition_earnings for current actual payment days
self.set_component_amounts_based_on_payment_days() current_taxable_earnings_for_payment_days = self.get_taxable_earnings(based_on_payment_days=1)
current_taxable_earnings_for_payment_days = self.get_taxable_earnings()
current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings
current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income
current_additional_earnings_with_full_tax = current_taxable_earnings_for_payment_days.additional_income_with_full_tax current_additional_earnings_with_full_tax = current_taxable_earnings_for_payment_days.additional_income_with_full_tax
@ -501,7 +502,6 @@ class SalarySlip(TransactionBase):
# Structured tax amount # Structured tax amount
total_structured_tax_amount = self.calculate_tax_by_tax_slab(payroll_period, total_taxable_earnings_without_full_tax_addl_components) total_structured_tax_amount = self.calculate_tax_by_tax_slab(payroll_period, total_taxable_earnings_without_full_tax_addl_components)
current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods
# Total taxable earnings with additional earnings with full tax # Total taxable earnings with additional earnings with full tax
@ -560,25 +560,39 @@ class SalarySlip(TransactionBase):
return total_tax_paid return total_tax_paid
def get_taxable_earnings(self, only_flexi=0): def get_taxable_earnings(self, based_on_payment_days=0):
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
if not relieving_date:
relieving_date = getdate(self.end_date)
if not joining_date:
frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
taxable_earnings = 0 taxable_earnings = 0
additional_income = 0 additional_income = 0
additional_income_with_full_tax = 0 additional_income_with_full_tax = 0
flexi_benefits = 0 flexi_benefits = 0
for earning in self.earnings: for earning in self.earnings:
if based_on_payment_days:
amount, additional_amount = self.get_amount_based_on_payment_days(earning, joining_date, relieving_date)
else:
amount, additional_amount = earning.amount, earning.additional_amount
if earning.is_tax_applicable: if earning.is_tax_applicable:
if flt(earning.additional_amount): if additional_amount:
taxable_earnings += (earning.amount - earning.additional_amount) taxable_earnings += (amount - additional_amount)
additional_income += earning.additional_amount additional_income += additional_amount
if earning.deduct_full_tax_on_selected_payroll_date: if earning.deduct_full_tax_on_selected_payroll_date:
additional_income_with_full_tax += earning.additional_amount additional_income_with_full_tax += additional_amount
continue continue
if earning.is_flexible_benefit: if earning.is_flexible_benefit:
flexi_benefits += earning.amount flexi_benefits += amount
else: else:
taxable_earnings += earning.amount taxable_earnings += amount
return frappe._dict({ return frappe._dict({
"taxable_earnings": taxable_earnings, "taxable_earnings": taxable_earnings,
@ -587,6 +601,26 @@ class SalarySlip(TransactionBase):
"flexi_benefits": flexi_benefits "flexi_benefits": flexi_benefits
}) })
def get_amount_based_on_payment_days(self, row, joining_date, relieving_date):
amount, additional_amount = row.amount, row.additional_amount
if (self.salary_structure and
cint(row.depends_on_payment_days) and cint(self.total_working_days) and
(not self.salary_slip_based_on_timesheet or
getdate(self.start_date) < joining_date or
getdate(self.end_date) > relieving_date
)):
additional_amount = flt((flt(row.additional_amount) * flt(self.payment_days)
/ cint(self.total_working_days)), row.precision("additional_amount"))
amount = flt((flt(row.default_amount) * flt(self.payment_days)
/ cint(self.total_working_days)), row.precision("amount")) + additional_amount
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
amount, additional_amount = 0, 0
elif not row.amount:
amount = row.default_amount + row.additional_amount
return amount, additional_amount
def calculate_unclaimed_taxable_benefits(self, payroll_period): def calculate_unclaimed_taxable_benefits(self, payroll_period):
# get total sum of benefits paid # get total sum of benefits paid
total_benefits_paid = flt(frappe.db.sql(""" total_benefits_paid = flt(frappe.db.sql("""
@ -688,7 +722,7 @@ class SalarySlip(TransactionBase):
return total return total
def set_component_amounts_based_on_payment_days(self): def set_component_amounts_based_on_payment_days(self):
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
["date_of_joining", "relieving_date"]) ["date_of_joining", "relieving_date"])
if not relieving_date: if not relieving_date:
@ -699,22 +733,7 @@ class SalarySlip(TransactionBase):
for component_type in ("earnings", "deductions"): for component_type in ("earnings", "deductions"):
for d in self.get(component_type): for d in self.get(component_type):
if (self.salary_structure and d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
cint(d.depends_on_payment_days) and cint(self.total_working_days) and
(not self.salary_slip_based_on_timesheet or
getdate(self.start_date) < joining_date or
getdate(self.end_date) > relieving_date
)):
d.amount = flt(
(flt(d.default_amount + d.additional_amount) * flt(self.payment_days)
/ cint(self.total_working_days))
, d.precision("amount"))
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(d.depends_on_payment_days):
d.amount = 0
elif not d.amount:
d.amount = d.default_amount + d.additional_amount
def set_loan_repayment(self): def set_loan_repayment(self):
self.set('loans', []) self.set('loans', [])

View File

@ -442,7 +442,8 @@ def make_deduction_salary_component(setup=False, test_tax=False):
"formula": 'base*.1', "formula": 'base*.1',
"type": "Deduction", "type": "Deduction",
"amount_based_on_formula": 1, "amount_based_on_formula": 1,
"depends_on_payment_days": 0 "depends_on_payment_days": 0,
"variable_based_on_taxable_salary": 1
} }
] ]
if not test_tax: if not test_tax:

View File

@ -14,6 +14,7 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
refresh: function() { refresh: function() {
this.frm.disable_save(); this.frm.disable_save();
this.show_upload(); this.show_upload();
this.setup_import_progress();
}, },
get_template:function() { get_template:function() {
@ -33,46 +34,36 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty(); var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty();
new frappe.ui.FileUploader({ new frappe.ui.FileUploader({
wrapper: $wrapper, wrapper: $wrapper,
method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload', method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload'
on_success(file_doc, r) {
var $log_wrapper = $(cur_frm.fields_dict.import_log.wrapper).empty();
if(!r.messages) r.messages = [];
// replace links if error has occured
if(r.exc || r.error) {
r.messages = $.map(r.message.messages, function(v) {
var msg = v.replace("Inserted", "Valid")
.replace("Updated", "Valid").split("<");
if (msg.length > 1) {
v = msg[0] + (msg[1].split(">").slice(-1)[0]);
} else {
v = msg[0];
}
return v;
});
r.messages = ["<h4 style='color:red'>"+__("Import Failed!")+"</h4>"]
.concat(r.messages);
} else {
r.messages = ["<h4 style='color:green'>"+__("Import Successful!")+"</h4>"]
.concat(r.message.messages);
}
$.each(r.messages, function(i, v) {
var $p = $('<p>').html(v).appendTo($log_wrapper);
if(v.substr(0,5)=='Error') {
$p.css('color', 'red');
} else if(v.substr(0,8)=='Inserted') {
$p.css('color', 'green');
} else if(v.substr(0,7)=='Updated') {
$p.css('color', 'green');
} else if(v.substr(0,5)=='Valid') {
$p.css('color', '#777');
}
});
}
}); });
}, },
setup_import_progress() {
var $log_wrapper = $(this.frm.fields_dict.import_log.wrapper).empty();
frappe.realtime.on('import_attendance', (data) => {
if (data.progress) {
this.frm.dashboard.show_progress('Import Attendance', data.progress / data.total * 100,
__('Importing {0} of {1}', [data.progress, data.total]));
if (data.progress === data.total) {
this.frm.dashboard.hide_progress('Import Attendance');
}
} else if (data.error) {
this.frm.dashboard.hide();
let messages = [`<th>${__('Error in some rows')}</th>`].concat(data.messages
.filter(message => message.includes('Error'))
.map(message => `<tr><td>${message}</td></tr>`))
.join('');
$log_wrapper.append('<table class="table table-bordered">' + messages);
} else if (data.messages) {
this.frm.dashboard.hide();
let messages = [`<th>${__('Import Successful')}</th>`].concat(data.messages
.map(message => `<tr><td>${message}</td></tr>`))
.join('');
$log_wrapper.append('<table class="table table-bordered">' + messages);
}
});
}
}) })
cur_frm.cscript = new erpnext.hr.AttendanceControlPanel({frm: cur_frm}); cur_frm.cscript = new erpnext.hr.AttendanceControlPanel({frm: cur_frm});

View File

@ -117,22 +117,25 @@ def upload():
raise frappe.PermissionError raise frappe.PermissionError
from frappe.utils.csvutils import read_csv_content from frappe.utils.csvutils import read_csv_content
rows = read_csv_content(frappe.local.uploaded_file)
if not rows:
frappe.throw(_("Please select a csv file"))
frappe.enqueue(import_attendances, rows=rows, now=True if len(rows) < 200 else False)
def import_attendances(rows):
from frappe.modules import scrub from frappe.modules import scrub
rows = read_csv_content(frappe.local.uploaded_file)
rows = list(filter(lambda x: x and any(x), rows)) rows = list(filter(lambda x: x and any(x), rows))
if not rows:
msg = [_("Please select a csv file")]
return {"messages": msg, "error": msg}
columns = [scrub(f) for f in rows[4]] columns = [scrub(f) for f in rows[4]]
columns[0] = "name" columns[0] = "name"
columns[3] = "attendance_date" columns[3] = "attendance_date"
rows = rows[5:]
ret = [] ret = []
error = False error = False
from frappe.utils.csvutils import check_record, import_doc from frappe.utils.csvutils import check_record, import_doc
for i, row in enumerate(rows[5:]): for i, row in enumerate(rows):
if not row: continue if not row: continue
row_idx = i + 5 row_idx = i + 5
d = frappe._dict(zip(columns, row)) d = frappe._dict(zip(columns, row))
@ -144,6 +147,10 @@ def upload():
try: try:
check_record(d) check_record(d)
ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True)) ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True))
frappe.publish_realtime('import_attendance', dict(
progress=i,
total=len(rows)
))
except AttributeError: except AttributeError:
pass pass
except Exception as e: except Exception as e:
@ -156,4 +163,8 @@ def upload():
frappe.db.rollback() frappe.db.rollback()
else: else:
frappe.db.commit() frappe.db.commit()
return {"messages": ret, "error": error}
frappe.publish_realtime('import_attendance', dict(
messages=ret,
error=error
))

View File

@ -12,3 +12,5 @@ class Vehicle(Document):
def validate(self): def validate(self):
if getdate(self.start_date) > getdate(self.end_date): if getdate(self.start_date) > getdate(self.end_date):
frappe.throw(_("Insurance Start date should be less than Insurance End date")) frappe.throw(_("Insurance Start date should be less than Insurance End date"))
if getdate(self.carbon_check_date) > getdate():
frappe.throw(_("Last carbon check date cannot be a future date"))

File diff suppressed because it is too large Load Diff

View File

@ -756,7 +756,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.get_exchange_rate(transaction_date, this.frm.doc.currency, company_currency, this.get_exchange_rate(transaction_date, this.frm.doc.currency, company_currency,
function(exchange_rate) { function(exchange_rate) {
if(exchange_rate != me.frm.doc.conversion_rate) {
me.set_margin_amount_based_on_currency(exchange_rate);
me.set_actual_charges_based_on_currency(exchange_rate);
me.frm.set_value("conversion_rate", exchange_rate); me.frm.set_value("conversion_rate", exchange_rate);
}
}); });
} else { } else {
this.conversion_rate(); this.conversion_rate();
@ -777,7 +781,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if(this.frm.doc.ignore_pricing_rule) { if(this.frm.doc.ignore_pricing_rule) {
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
} else if (!this.in_apply_price_list){ } else if (!this.in_apply_price_list){
this.set_actual_charges_based_on_currency();
this.apply_price_list(); this.apply_price_list();
} }
@ -804,12 +807,24 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
} }
}, },
set_actual_charges_based_on_currency: function() { set_margin_amount_based_on_currency: function(exchange_rate) {
if (in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]), this.frm.doc.doctype) {
var me = this;
$.each(this.frm.doc.items || [], function(i, d) {
if(d.margin_type == "Amount") {
frappe.model.set_value(d.doctype, d.name, "margin_rate_or_amount",
flt(d.margin_rate_or_amount) / flt(exchange_rate));
}
});
}
},
set_actual_charges_based_on_currency: function(exchange_rate) {
var me = this; var me = this;
$.each(this.frm.doc.taxes || [], function(i, d) { $.each(this.frm.doc.taxes || [], function(i, d) {
if(d.charge_type == "Actual") { if(d.charge_type == "Actual") {
frappe.model.set_value(d.doctype, d.name, "tax_amount", frappe.model.set_value(d.doctype, d.name, "tax_amount",
flt(d.tax_amount) / flt(me.frm.doc.conversion_rate)); flt(d.tax_amount) / flt(exchange_rate));
} }
}); });
}, },

View File

@ -21,7 +21,7 @@
<hr> <hr>
<div class="row"> <div class="row">
<div class="col-sm-6 "> <div class="col-sm-6 ">
<div class ="row multimode-payments"> <div class ="row multimode-payments" style = "margin-right:10px">
</div> </div>
</div> </div>
<div class="col-sm-6 payment-toolbar"> <div class="col-sm-6 payment-toolbar">

View File

@ -67,6 +67,7 @@ class Gstr1Report(object):
row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N") row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N")
row.append("C" if invoice_details.return_against else "R") row.append("C" if invoice_details.return_against else "R")
if taxable_value:
self.data.append(row) self.data.append(row)
def get_b2cs_data(self): def get_b2cs_data(self):
@ -113,9 +114,14 @@ class Gstr1Report(object):
row.append(export_type) row.append(export_type)
else: else:
row.append(invoice_details.get(fieldname)) row.append(invoice_details.get(fieldname))
taxable_value = 0
for item_code, net_amount in self.invoice_items.get(invoice).items():
if item_code in items:
if self.item_tax_rate.get(invoice) and tax_rate == self.item_tax_rate.get(invoice, {}).get(item_code):
taxable_value += abs(net_amount)
elif not self.item_tax_rate.get(invoice):
taxable_value += abs(net_amount)
taxable_value = sum([abs(net_amount)
for item_code, net_amount in self.invoice_items.get(invoice).items() if item_code in items])
row += [tax_rate or 0, taxable_value] row += [tax_rate or 0, taxable_value]
return row, taxable_value return row, taxable_value
@ -184,8 +190,10 @@ class Gstr1Report(object):
def get_invoice_items(self): def get_invoice_items(self):
self.invoice_items = frappe._dict() self.invoice_items = frappe._dict()
self.item_tax_rate = frappe._dict()
items = frappe.db.sql(""" items = frappe.db.sql("""
select item_code, parent, base_net_amount select item_code, parent, base_net_amount, item_tax_rate
from `tab%s Item` from `tab%s Item`
where parent in (%s) where parent in (%s)
""" % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1) """ % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1)
@ -196,6 +204,12 @@ class Gstr1Report(object):
sum(i.get('base_net_amount', 0) for i in items sum(i.get('base_net_amount', 0) for i in items
if i.item_code == d.item_code and i.parent == d.parent)) if i.item_code == d.item_code and i.parent == d.parent))
item_tax_rate = json.loads(d.item_tax_rate)
if item_tax_rate:
for account, rate in item_tax_rate.items():
self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, rate)
def get_items_based_on_tax_rate(self): def get_items_based_on_tax_rate(self):
self.tax_details = frappe.db.sql(""" self.tax_details = frappe.db.sql("""
select select

View File

@ -1,5 +1,5 @@
{ {
"add_total_row": 1, "add_total_row": 0,
"creation": "2018-09-21 12:46:29.451048", "creation": "2018-09-21 12:46:29.451048",
"disable_prepared_report": 0, "disable_prepared_report": 0,
"disabled": 0, "disabled": 0,
@ -7,7 +7,7 @@
"doctype": "Report", "doctype": "Report",
"idx": 0, "idx": 0,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2019-02-12 14:30:40.043652", "modified": "2019-05-24 05:37:02.866139",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Analytics", "name": "Sales Analytics",

View File

@ -3093,7 +3093,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2019-03-26 17:15:50.390548", "modified": "2019-05-21 17:15:50.390548",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Company", "name": "Company",

View File

@ -135,8 +135,8 @@ class NamingSeries(Document):
def validate_series_name(self, n): def validate_series_name(self, n):
import re import re
if not re.match("^[\w\- /.#]*$", n, re.UNICODE): if not re.match("^[\w\- /.#{}]*$", n, re.UNICODE):
throw(_('Special Characters except "-", "#", "." and "/" not allowed in naming series')) throw(_('Special Characters except "-", "#", ".", "/", "{" and "}" not allowed in naming series'))
def get_options(self, arg=None): def get_options(self, arg=None):
if frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series"): if frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series"):

View File

@ -64,7 +64,10 @@ class DeliveryNote(SellingController):
'second_source_dt': 'Sales Invoice Item', 'second_source_dt': 'Sales Invoice Item',
'second_source_field': '-1 * qty', 'second_source_field': '-1 * qty',
'second_join_field': 'so_detail', 'second_join_field': 'so_detail',
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)""" 'extra_cond': """ and exists (select name from `tabDelivery Note`
where name=`tabDelivery Note Item`.parent and is_return=1)""",
'second_source_extra_cond': """ and exists (select name from `tabSales Invoice`
where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)"""
}) })
def before_print(self): def before_print(self):

View File

@ -233,6 +233,8 @@ def update_completed_and_requested_qty(stock_entry, method):
mr_obj.update_requested_qty(mr_item_rows) mr_obj.update_requested_qty(mr_item_rows)
def set_missing_values(source, target_doc): def set_missing_values(source, target_doc):
if target_doc.doctype == "Purchase Order" and getdate(target_doc.schedule_date) < getdate(nowdate()):
target_doc.schedule_date = None
target_doc.run_method("set_missing_values") target_doc.run_method("set_missing_values")
target_doc.run_method("calculate_taxes_and_totals") target_doc.run_method("calculate_taxes_and_totals")
@ -240,6 +242,8 @@ def update_item(obj, target, source_parent):
target.conversion_factor = obj.conversion_factor target.conversion_factor = obj.conversion_factor
target.qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty))/ target.conversion_factor target.qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty))/ target.conversion_factor
target.stock_qty = (target.qty * target.conversion_factor) target.stock_qty = (target.qty * target.conversion_factor)
if getdate(target.schedule_date) < getdate(nowdate()):
target.schedule_date = None
def get_list_context(context=None): def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context from erpnext.controllers.website_list_for_contact import get_list_context
@ -336,7 +340,8 @@ def make_purchase_order_based_on_supplier(source_name, target_doc=None):
def postprocess(source, target_doc): def postprocess(source, target_doc):
target_doc.supplier = source_name target_doc.supplier = source_name
target_doc.schedule_date = add_days(nowdate(), 1) if getdate(target_doc.schedule_date) < getdate(nowdate()):
target_doc.schedule_date = None
target_doc.set("items", [d for d in target_doc.get("items") target_doc.set("items", [d for d in target_doc.get("items")
if d.get("item_code") in supplier_items and d.get("qty") > 0]) if d.get("item_code") in supplier_items and d.get("qty") > 0])

View File

@ -1,755 +1,186 @@
{ {
"allow_copy": 0, "allow_import": 1,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "MAT-PAC-.YYYY.-.#####", "autoname": "MAT-PAC-.YYYY.-.#####",
"beta": 0,
"creation": "2013-04-11 15:32:24", "creation": "2013-04-11 15:32:24",
"custom": 0,
"description": "Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.", "description": "Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.",
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Document", "document_type": "Document",
"editable_grid": 0, "engine": "InnoDB",
"field_order": [
"packing_slip_details",
"column_break0",
"delivery_note",
"column_break1",
"naming_series",
"section_break0",
"column_break2",
"from_case_no",
"column_break3",
"to_case_no",
"package_item_details",
"get_items",
"items",
"package_weight_details",
"net_weight_pkg",
"net_weight_uom",
"column_break4",
"gross_weight_pkg",
"gross_weight_uom",
"letter_head_details",
"letter_head",
"misc_details",
"amended_from"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "packing_slip_details", "fieldname": "packing_slip_details",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break0", "fieldname": "column_break0",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Indicates that the package is a part of this delivery (Only Draft)", "description": "Indicates that the package is a part of this delivery (Only Draft)",
"fieldname": "delivery_note", "fieldname": "delivery_note",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1, "in_global_search": 1,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Delivery Note", "label": "Delivery Note",
"length": 0,
"no_copy": 0,
"options": "Delivery Note", "options": "Delivery Note",
"permlevel": 0, "reqd": 1
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break1", "fieldname": "column_break1",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "naming_series", "fieldname": "naming_series",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series", "label": "Series",
"length": 0,
"no_copy": 0,
"options": "MAT-PAC-.YYYY.-", "options": "MAT-PAC-.YYYY.-",
"permlevel": 0,
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "set_only_once": 1
"set_only_once": 1,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break0", "fieldname": "section_break0",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break2", "fieldname": "column_break2",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Identification of the package for the delivery (for print)", "description": "Identification of the package for the delivery (for print)",
"fieldname": "from_case_no", "fieldname": "from_case_no",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "From Package No.", "label": "From Package No.",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50px" "width": "50px"
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break3", "fieldname": "column_break3",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "If more than one package of the same type (for print)", "description": "If more than one package of the same type (for print)",
"fieldname": "to_case_no", "fieldname": "to_case_no",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "To Package No.", "label": "To Package No.",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50px" "width": "50px"
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "package_item_details", "fieldname": "package_item_details",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "get_items", "fieldname": "get_items",
"fieldtype": "Button", "fieldtype": "Button",
"hidden": 0, "label": "Get Items"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Get Items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "items", "fieldname": "items",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Items", "label": "Items",
"length": 0,
"no_copy": 0,
"options": "Packing Slip Item", "options": "Packing Slip Item",
"permlevel": 0, "reqd": 1
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "package_weight_details", "fieldname": "package_weight_details",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Package Weight Details"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Package Weight Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "The net weight of this package. (calculated automatically as sum of net weight of items)", "description": "The net weight of this package. (calculated automatically as sum of net weight of items)",
"fieldname": "net_weight_pkg", "fieldname": "net_weight_pkg",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Net Weight", "label": "Net Weight",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0, "read_only": 1
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "net_weight_uom", "fieldname": "net_weight_uom",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Net Weight UOM", "label": "Net Weight UOM",
"length": 0,
"no_copy": 1, "no_copy": 1,
"options": "UOM", "options": "UOM",
"permlevel": 0, "read_only": 1
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break4", "fieldname": "column_break4",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "The gross weight of the package. Usually net weight + packaging material weight. (for print)", "description": "The gross weight of the package. Usually net weight + packaging material weight. (for print)",
"fieldname": "gross_weight_pkg", "fieldname": "gross_weight_pkg",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Gross Weight", "label": "Gross Weight",
"length": 0, "no_copy": 1
"no_copy": 1,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "gross_weight_uom", "fieldname": "gross_weight_uom",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Gross Weight UOM", "label": "Gross Weight UOM",
"length": 0,
"no_copy": 1, "no_copy": 1,
"options": "UOM", "options": "UOM"
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "letter_head_details", "fieldname": "letter_head_details",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Letter Head"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Letter Head",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "letter_head", "fieldname": "letter_head",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Letter Head", "label": "Letter Head",
"length": 0,
"no_copy": 0,
"options": "Letter Head", "options": "Letter Head",
"permlevel": 0, "print_hide": 1
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "misc_details", "fieldname": "misc_details",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from", "fieldname": "amended_from",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From", "label": "Amended From",
"length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Packing Slip", "options": "Packing Slip",
"permlevel": 0,
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "read_only": 1
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-suitcase", "icon": "fa fa-suitcase",
"idx": 1, "idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "modified": "2019-05-31 04:45:08.082862",
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 16:15:37.201278",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Packing Slip", "name": "Packing Slip",
@ -761,15 +192,10 @@
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Stock User", "role": "Stock User",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 1, "submit": 1,
"write": 1 "write": 1
@ -780,15 +206,10 @@
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Sales User", "role": "Sales User",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 1, "submit": 1,
"write": 1 "write": 1
@ -799,15 +220,10 @@
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Item Manager", "role": "Item Manager",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 1, "submit": 1,
"write": 1 "write": 1
@ -818,15 +234,10 @@
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Stock Manager", "role": "Stock Manager",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 1, "submit": 1,
"write": 1 "write": 1
@ -837,27 +248,17 @@
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Sales Manager", "role": "Sales Manager",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 1, "submit": 1,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 1,
"search_fields": "delivery_note", "search_fields": "delivery_note",
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_order": "DESC", "sort_field": "modified",
"track_changes": 0, "sort_order": "DESC"
"track_seen": 0,
"track_views": 0
} }

View File

@ -10,7 +10,7 @@ frappe.ui.form.on("Purchase Receipt", {
frm.custom_make_buttons = { frm.custom_make_buttons = {
'Stock Entry': 'Return', 'Stock Entry': 'Return',
'Purchase Invoice': 'Invoice' 'Purchase Invoice': 'Invoice'
} };
frm.set_query("asset", "items", function() { frm.set_query("asset", "items", function() {
return { return {
@ -18,7 +18,15 @@ frappe.ui.form.on("Purchase Receipt", {
"purchase_receipt": frm.doc.name "purchase_receipt": frm.doc.name
} }
} }
}) });
frm.set_query("expense_account", "items", function() {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: {'company': frm.doc.company}
}
});
}, },
onload: function(frm) { onload: function(frm) {
erpnext.queries.setup_queries(frm, "Warehouse", function() { erpnext.queries.setup_queries(frm, "Warehouse", function() {

View File

@ -36,7 +36,9 @@ class PurchaseReceipt(BuyingController):
'second_source_field': 'received_qty', 'second_source_field': 'received_qty',
'second_join_field': 'po_detail', 'second_join_field': 'po_detail',
'percent_join_field': 'purchase_order', 'percent_join_field': 'purchase_order',
'overflow_type': 'receipt' 'overflow_type': 'receipt',
'second_source_extra_cond': """ and exists(select name from `tabPurchase Invoice`
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
}, },
{ {
'source_dt': 'Purchase Receipt Item', 'source_dt': 'Purchase Receipt Item',
@ -55,10 +57,7 @@ class PurchaseReceipt(BuyingController):
'join_field': 'purchase_order_item', 'join_field': 'purchase_order_item',
'target_field': 'returned_qty', 'target_field': 'returned_qty',
'target_parent_dt': 'Purchase Order', 'target_parent_dt': 'Purchase Order',
# 'target_parent_field': 'per_received',
# 'target_ref_field': 'qty',
'source_field': '-1 * qty', 'source_field': '-1 * qty',
# 'overflow_type': 'receipt',
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)""" 'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
}] }]
if cint(self.is_return): if cint(self.is_return):
@ -71,7 +70,10 @@ class PurchaseReceipt(BuyingController):
'second_source_dt': 'Purchase Invoice Item', 'second_source_dt': 'Purchase Invoice Item',
'second_source_field': '-1 * qty', 'second_source_field': '-1 * qty',
'second_join_field': 'po_detail', 'second_join_field': 'po_detail',
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)""" 'extra_cond': """ and exists (select name from `tabPurchase Receipt`
where name=`tabPurchase Receipt Item`.parent and is_return=1)""",
'second_source_extra_cond': """ and exists (select name from `tabPurchase Invoice`
where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)"""
}) })
def validate(self): def validate(self):

View File

@ -4,6 +4,7 @@
"doctype": "DocType", "doctype": "DocType",
"document_type": "Document", "document_type": "Document",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB",
"field_order": [ "field_order": [
"barcode", "barcode",
"section_break_2", "section_break_2",
@ -80,6 +81,7 @@
"rejected_serial_no", "rejected_serial_no",
"section_break_50", "section_break_50",
"project", "project",
"expense_account",
"cost_center", "cost_center",
"col_break5", "col_break5",
"allow_zero_valuation_rate", "allow_zero_valuation_rate",
@ -245,6 +247,7 @@
"width": "100px" "width": "100px"
}, },
{ {
"default": "0",
"fetch_from": "item_code.retain_sample", "fetch_from": "item_code.retain_sample",
"fieldname": "retain_sample", "fieldname": "retain_sample",
"fieldtype": "Check", "fieldtype": "Check",
@ -361,6 +364,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"default": "0",
"fieldname": "is_free_item", "fieldname": "is_free_item",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Free Item", "label": "Is Free Item",
@ -482,6 +486,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"default": "0",
"fieldname": "is_fixed_asset", "fieldname": "is_fixed_asset",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 1, "hidden": 1,
@ -620,6 +625,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"default": "0",
"fieldname": "allow_zero_valuation_rate", "fieldname": "allow_zero_valuation_rate",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Allow Zero Valuation Rate", "label": "Allow Zero Valuation Rate",
@ -635,6 +641,7 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"default": "0",
"depends_on": "eval:parent.is_subcontracted == 'Yes'", "depends_on": "eval:parent.is_subcontracted == 'Yes'",
"fieldname": "include_exploded_items", "fieldname": "include_exploded_items",
"fieldtype": "Check", "fieldtype": "Check",
@ -739,6 +746,7 @@
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"default": "0",
"fieldname": "page_break", "fieldname": "page_break",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Page Break", "label": "Page Break",
@ -766,11 +774,19 @@
"fieldname": "material_request_item", "fieldname": "material_request_item",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Material Request Item" "label": "Material Request Item"
},
{
"fieldname": "expense_account",
"fieldtype": "Link",
"hidden": 1,
"label": "Expense Account",
"options": "Account",
"read_only": 1
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2019-05-08 10:25:27.157675", "modified": "2019-05-30 18:09:21.648599",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt Item", "name": "Purchase Receipt Item",

View File

@ -1963,6 +1963,40 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_opening",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Opening",
"length": 0,
"no_copy": 0,
"options": "No\nYes",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -2246,7 +2280,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2019-03-26 12:24:46.439626", "modified": "2019-05-23 12:24:46.439626",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock Entry", "name": "Stock Entry",
@ -2339,4 +2373,4 @@
"track_changes": 1, "track_changes": 1,
"track_seen": 0, "track_seen": 0,
"track_views": 0 "track_views": 0
} }

View File

@ -17,6 +17,7 @@ from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, add_additiona
from erpnext.stock.utils import get_bin from erpnext.stock.utils import get_bin
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
import json import json
@ -61,6 +62,7 @@ class StockEntry(StockController):
self.validate_batch() self.validate_batch()
self.validate_inspection() self.validate_inspection()
self.validate_fg_completed_qty() self.validate_fg_completed_qty()
self.validate_difference_account()
self.set_job_card_data() self.set_job_card_data()
self.set_purpose_for_stock_entry() self.set_purpose_for_stock_entry()
@ -226,7 +228,18 @@ class StockEntry(StockController):
production_item = frappe.get_value('Work Order', self.work_order, 'production_item') production_item = frappe.get_value('Work Order', self.work_order, 'production_item')
for item in self.items: for item in self.items:
if item.item_code == production_item and item.qty != self.fg_completed_qty: if item.item_code == production_item and item.qty != self.fg_completed_qty:
frappe.throw(_("Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different").format(item.qty, self.fg_completed_qty)) frappe.throw(_("Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different")
.format(item.qty, self.fg_completed_qty))
def validate_difference_account(self):
if not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
return
for d in self.get("items"):
if not d.expense_account:
frappe.throw(_("Please enter Difference Account"))
elif self.is_opening == "Yes" and frappe.db.get_value("Account", d.expense_account, "report_type") == "Profit and Loss":
frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry"), OpeningEntryAccountError)
def validate_warehouse(self): def validate_warehouse(self):
"""perform various (sometimes conditional) validations on warehouse""" """perform various (sometimes conditional) validations on warehouse"""

View File

@ -89,10 +89,11 @@ def make_stock_entry(**args):
s.purchase_receipt_no = args.purchase_receipt_no s.purchase_receipt_no = args.purchase_receipt_no
s.delivery_note_no = args.delivery_note_no s.delivery_note_no = args.delivery_note_no
s.sales_invoice_no = args.sales_invoice_no s.sales_invoice_no = args.sales_invoice_no
s.is_opening = args.is_opening or "No"
if not args.cost_center: if not args.cost_center:
args.cost_center = frappe.get_value('Company', s.company, 'cost_center') args.cost_center = frappe.get_value('Company', s.company, 'cost_center')
if not args.expense_account: if not args.expense_account and s.is_opening == "No":
args.expense_account = frappe.get_value('Company', s.company, 'stock_adjustment_account') args.expense_account = frappe.get_value('Company', s.company, 'stock_adjustment_account')
# We can find out the serial number using the batch source document # We can find out the serial number using the batch source document

View File

@ -6,8 +6,7 @@ import frappe, unittest
import frappe.defaults import frappe.defaults
from frappe.utils import flt, nowdate, nowtime from frappe.utils import flt, nowdate, nowtime
from erpnext.stock.doctype.serial_no.serial_no import * from erpnext.stock.doctype.serial_no.serial_no import *
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ from erpnext import set_perpetual_inventory
import set_perpetual_inventory
from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
from erpnext.stock.stock_ledger import get_previous_sle from erpnext.stock.stock_ledger import get_previous_sle
from frappe.permissions import add_user_permission, remove_user_permission from frappe.permissions import add_user_permission, remove_user_permission
@ -16,6 +15,7 @@ from erpnext.stock.doctype.item.test_item import set_item_variant_settings, make
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse, make_stock_in_entry from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse, make_stock_in_entry
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
from six import iteritems from six import iteritems
@ -772,6 +772,22 @@ class TestStockEntry(unittest.TestCase):
doc = frappe.get_doc('Stock Entry', outward_entry.name) doc = frappe.get_doc('Stock Entry', outward_entry.name)
self.assertEqual(doc.per_transferred, 100) self.assertEqual(doc.per_transferred, 100)
def test_gle_for_opening_stock_entry(self):
set_perpetual_inventory(1)
mr = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC",
qty=50, basic_rate=100, expense_account="Stock Adjustment - _TC", is_opening="Yes", do_not_save=True)
self.assertRaises(OpeningEntryAccountError, mr.save)
mr.items[0].expense_account = "Temporary Opening - _TC"
mr.save()
mr.submit()
is_opening = frappe.db.get_value("GL Entry",
filters={"voucher_type": "Stock Entry", "voucher_no": mr.name}, fieldname="is_opening")
self.assertEqual(is_opening, "Yes")
def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None): def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None):
se = frappe.copy_doc(test_records[0]) se = frappe.copy_doc(test_records[0])
se.get("items")[0].item_code = item_code or "_Test Serialized Item With Series" se.get("items")[0].item_code = item_code or "_Test Serialized Item With Series"

View File

@ -238,8 +238,8 @@ class StockReconciliation(StockController):
return return
if not self.expense_account: if not self.expense_account:
msgprint(_("Please enter Expense Account"), raise_exception=1) frappe.throw(_("Please enter Expense Account"))
elif not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""): elif self.purpose == "Opening Stock" or not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""):
if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss": if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry"), OpeningEntryAccountError) frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry"), OpeningEntryAccountError)
@ -276,7 +276,8 @@ def get_items(warehouse, posting_date, posting_time, company):
items = frappe.db.sql(""" items = frappe.db.sql("""
select i.name, i.item_name, bin.warehouse select i.name, i.item_name, bin.warehouse
from tabBin bin, tabItem i from tabBin bin, tabItem i
where i.name=bin.item_code and i.disabled=0 where i.name=bin.item_code and i.disabled=0 and i.is_stock_item = 1
and i.has_variants = 0 and i.has_serial_no = 0 and i.has_batch_no = 0
and exists(select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=bin.warehouse) and exists(select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=bin.warehouse)
""", (lft, rgt)) """, (lft, rgt))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestWarehouseType(unittest.TestCase):
pass

View File

@ -0,0 +1,8 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Warehouse Type', {
// refresh: function(frm) {
// }
});

View File

@ -0,0 +1,108 @@
{
"autoname": "Prompt",
"creation": "2019-05-21 15:27:06.514511",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"description"
],
"fields": [
{
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Description"
}
],
"modified": "2019-05-27 18:35:33.354571",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse Type",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Item Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock Manager",
"share": 1,
"write": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"share": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock User",
"share": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase User",
"share": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing User",
"share": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class WarehouseType(Document):
pass

View File

@ -0,0 +1,58 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
// For license information, please see license.txt
frappe.query_reports["Product Bundle Balance"] = {
"filters": [
{
"fieldname":"date",
"label": __("Date"),
"fieldtype": "Date",
"width": "80",
"reqd": 1,
"default": frappe.datetime.get_today(),
},
{
"fieldname": "item_code",
"label": __("Item"),
"fieldtype": "Link",
"width": "80",
"options": "Item",
"get_query": function() {
return {
query: "erpnext.controllers.queries.item_query",
filters: {"is_stock_item": 0}
};
}
},
{
"fieldname": "item_group",
"label": __("Item Group"),
"fieldtype": "Link",
"width": "80",
"options": "Item Group"
},
{
"fieldname":"brand",
"label": __("Brand"),
"fieldtype": "Link",
"options": "Brand"
},
{
"fieldname": "warehouse",
"label": __("Warehouse"),
"fieldtype": "Link",
"width": "80",
"options": "Warehouse"
},
],
"initial_depth": 0,
"formatter": function(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
if (!data.parent_item) {
value = $(`<span>${value}</span>`);
var $value = $(value).css("font-weight", "bold");
value = $value.wrap("<p></p>").parent().html();
}
return value;
}
};

View File

@ -0,0 +1,23 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2019-03-06 01:40:35.418304",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2019-03-06 01:40:35.418304",
"modified_by": "Administrator",
"module": "Stock",
"name": "Product Bundle Balance",
"owner": "Administrator",
"ref_doctype": "Stock Ledger Entry",
"report_name": "Product Bundle Balance",
"report_type": "Script Report",
"roles": [
{
"role": "Stock User"
}
]
}

View File

@ -0,0 +1,187 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt
from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
from six import iteritems
def execute(filters=None):
if not filters:
filters = frappe._dict()
columns = get_columns()
item_details, pb_details, parent_items, child_items = get_items(filters)
stock_balance = get_stock_balance(filters, child_items)
data = []
for parent_item in parent_items:
parent_item_detail = item_details[parent_item]
required_items = pb_details[parent_item]
warehouse_company_map = {}
for child_item in required_items:
child_item_balance = stock_balance.get(child_item.item_code, frappe._dict())
for warehouse, sle in iteritems(child_item_balance):
if flt(sle.qty_after_transaction) > 0:
warehouse_company_map[warehouse] = sle.company
for warehouse, company in iteritems(warehouse_company_map):
parent_row = {
"indent": 0,
"item_code": parent_item,
"item_name": parent_item_detail.item_name,
"item_group": parent_item_detail.item_group,
"brand": parent_item_detail.brand,
"description": parent_item_detail.description,
"warehouse": warehouse,
"uom": parent_item_detail.stock_uom,
"company": company,
}
child_rows = []
for child_item_detail in required_items:
child_item_balance = stock_balance.get(child_item_detail.item_code, frappe._dict()).get(warehouse, frappe._dict())
child_row = {
"indent": 1,
"parent_item": parent_item,
"item_code": child_item_detail.item_code,
"item_name": child_item_detail.item_name,
"item_group": child_item_detail.item_group,
"brand": child_item_detail.brand,
"description": child_item_detail.description,
"warehouse": warehouse,
"uom": child_item_detail.uom,
"actual_qty": flt(child_item_balance.qty_after_transaction),
"minimum_qty": flt(child_item_detail.qty),
"company": company,
}
child_row["bundle_qty"] = child_row["actual_qty"] // child_row["minimum_qty"]
child_rows.append(child_row)
min_bundle_qty = min(map(lambda d: d["bundle_qty"], child_rows))
parent_row["bundle_qty"] = min_bundle_qty
data.append(parent_row)
data += child_rows
return columns, data
def get_columns():
columns = [
{"fieldname": "item_code", "label": _("Item"), "fieldtype": "Link", "options": "Item", "width": 300},
{"fieldname": "warehouse", "label": _("Warehouse"), "fieldtype": "Link", "options": "Warehouse", "width": 100},
{"fieldname": "uom", "label": _("UOM"), "fieldtype": "Link", "options": "UOM", "width": 70},
{"fieldname": "bundle_qty", "label": _("Bundle Qty"), "fieldtype": "Float", "width": 100},
{"fieldname": "actual_qty", "label": _("Actual Qty"), "fieldtype": "Float", "width": 100},
{"fieldname": "minimum_qty", "label": _("Minimum Qty"), "fieldtype": "Float", "width": 100},
{"fieldname": "item_group", "label": _("Item Group"), "fieldtype": "Link", "options": "Item Group", "width": 100},
{"fieldname": "brand", "label": _("Brand"), "fieldtype": "Link", "options": "Brand", "width": 100},
{"fieldname": "description", "label": _("Description"), "width": 140},
{"fieldname": "company", "label": _("Company"), "fieldtype": "Link", "options": "Company", "width": 100}
]
return columns
def get_items(filters):
pb_details = frappe._dict()
item_details = frappe._dict()
conditions = get_parent_item_conditions(filters)
parent_item_details = frappe.db.sql("""
select item.name as item_code, item.item_name, pb.description, item.item_group, item.brand, item.stock_uom
from `tabItem` item
inner join `tabProduct Bundle` pb on pb.new_item_code = item.name
where ifnull(item.disabled, 0) = 0 {0}
""".format(conditions), filters, as_dict=1) # nosec
parent_items = []
for d in parent_item_details:
parent_items.append(d.item_code)
item_details[d.item_code] = d
if parent_items:
child_item_details = frappe.db.sql("""
select
pb.new_item_code as parent_item, pbi.item_code, item.item_name, pbi.description, item.item_group, item.brand,
item.stock_uom, pbi.uom, pbi.qty
from `tabProduct Bundle Item` pbi
inner join `tabProduct Bundle` pb on pb.name = pbi.parent
inner join `tabItem` item on item.name = pbi.item_code
where pb.new_item_code in ({0})
""".format(", ".join(["%s"] * len(parent_items))), parent_items, as_dict=1) # nosec
else:
child_item_details = []
child_items = set()
for d in child_item_details:
if d.item_code != d.parent_item:
pb_details.setdefault(d.parent_item, []).append(d)
child_items.add(d.item_code)
item_details[d.item_code] = d
child_items = list(child_items)
return item_details, pb_details, parent_items, child_items
def get_stock_balance(filters, items):
sle = get_stock_ledger_entries(filters, items)
stock_balance = frappe._dict()
for d in sle:
stock_balance.setdefault(d.item_code, frappe._dict())[d.warehouse] = d
return stock_balance
def get_stock_ledger_entries(filters, items):
if not items:
return []
item_conditions_sql = ' and sle.item_code in ({})' \
.format(', '.join([frappe.db.escape(i) for i in items]))
conditions = get_sle_conditions(filters)
return frappe.db.sql("""
select
sle.item_code, sle.warehouse, sle.qty_after_transaction, sle.company
from
`tabStock Ledger Entry` sle force index (posting_sort_index)
left join `tabStock Ledger Entry` sle2 on
sle.item_code = sle2.item_code and sle.warehouse = sle2.warehouse
and (sle.posting_date, sle.posting_time, sle.name) < (sle2.posting_date, sle2.posting_time, sle2.name)
where sle2.name is null and sle.docstatus < 2 %s %s""" % (item_conditions_sql, conditions), as_dict=1) # nosec
def get_parent_item_conditions(filters):
conditions = []
if filters.get("item_code"):
conditions.append("item.item_code = %(item_code)s")
else:
if filters.get("brand"):
conditions.append("item.brand=%(brand)s")
if filters.get("item_group"):
conditions.append(get_item_group_condition(filters.get("item_group")))
conditions = " and ".join(conditions)
return "and {0}".format(conditions) if conditions else ""
def get_sle_conditions(filters):
conditions = ""
if not filters.get("date"):
frappe.throw(_("'Date' is required"))
conditions += " and sle.posting_date <= %s" % frappe.db.escape(filters.get("date"))
if filters.get("warehouse"):
warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
if warehouse_details:
conditions += " and exists (select name from `tabWarehouse` wh \
where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)" % (warehouse_details.lft, warehouse_details.rgt) # nosec
return conditions

View File

@ -49,7 +49,24 @@ frappe.query_reports["Stock Balance"] = {
"label": __("Warehouse"), "label": __("Warehouse"),
"fieldtype": "Link", "fieldtype": "Link",
"width": "80", "width": "80",
"options": "Warehouse" "options": "Warehouse",
get_query: () => {
var warehouse_type = frappe.query_report.get_filter_value('warehouse_type');
if(warehouse_type){
return {
filters: {
'warehouse_type': warehouse_type
}
}
}
}
},
{
"fieldname": "warehouse_type",
"label": __("Warehouse Type"),
"fieldtype": "Link",
"width": "80",
"options": "Warehouse Type"
}, },
{ {
"fieldname":"include_uom", "fieldname":"include_uom",

View File

@ -113,6 +113,10 @@ def get_conditions(filters):
where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(warehouse_details.lft, where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(warehouse_details.lft,
warehouse_details.rgt) warehouse_details.rgt)
if filters.get("warehouse_type") and not filters.get("warehouse"):
conditions += " and exists (select name from `tabWarehouse` wh \
where wh.warehouse_type = '%s' and sle.warehouse = wh.name)"%(filters.get("warehouse_type"))
return conditions return conditions
def get_stock_ledger_entries(filters, items): def get_stock_ledger_entries(filters, items):

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
DocType: Account,Accounts,དངུལ་རྩིས།
DocType: Pricing Rule,Buying,ཉོ་བ།
1 DocType: Account Accounts དངུལ་རྩིས།
DocType: Account Accounts དངུལ་རྩིས།
DocType: Pricing Rule Buying ཉོ་བ།

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
DocType: Patient,Married,既婚
1 DocType: Patient Married 既婚
DocType: Patient Married 既婚

View File

View File

File diff suppressed because it is too large Load Diff

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More