Merge branch 'v12-pre-release' into version-12

This commit is contained in:
Sahil Khan
2020-01-16 14:30:35 +05:30
114 changed files with 3973 additions and 8492 deletions

View File

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

View File

@@ -41,8 +41,8 @@ class AccountingPeriod(Document):
def get_doctypes_for_closing(self):
docs_for_closing = []
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation",
"Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
"Bank Reconciliation", "Asset", "Stock Entry"]
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
for closed_doctype in closed_doctypes:
docs_for_closing.append(closed_doctype)

View File

@@ -29,7 +29,6 @@ class GLEntry(Document):
self.validate_and_set_fiscal_year()
self.pl_must_have_cost_center()
self.validate_cost_center()
self.validate_dimensions_for_pl_and_bs()
if not self.flags.from_repost:
self.check_pl_account()
@@ -39,6 +38,7 @@ class GLEntry(Document):
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
if not from_repost:
self.validate_account_details(adv_adj)
self.validate_dimensions_for_pl_and_bs()
check_freezing_date(self.posting_date, adv_adj)
validate_frozen_account(self.account, adv_adj)

View File

@@ -190,7 +190,6 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
if(jvd.reference_type==="Employee Advance") {
return {
filters: {
'status': ['=', 'Unpaid'],
'docstatus': 1
}
};

View File

@@ -9,7 +9,6 @@ from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.accounts.party import get_party_account
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.hr.doctype.loan.loan import update_disbursement_status, update_total_amount_paid
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
from six import string_types, iteritems
@@ -606,8 +605,8 @@ class JournalEntry(AccountsController):
for d in self.accounts:
if d.reference_type=="Loan" and flt(d.debit) > 0:
doc = frappe.get_doc("Loan", d.reference_name)
update_disbursement_status(doc)
update_total_amount_paid(doc)
doc.update_total_amount_paid()
doc.set_status()
def validate_expense_claim(self):
for d in self.accounts:
@@ -968,7 +967,7 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
# The date used to retreive the exchange rate here is the date passed
# in as an argument to this function.
elif (not exchange_rate or exchange_rate==1) and account_currency and posting_date:
elif (not exchange_rate or flt(exchange_rate)==1) and account_currency and posting_date:
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
else:
exchange_rate = 1

View File

@@ -90,7 +90,6 @@
"fieldtype": "Column Break"
},
{
"default": "Customer",
"fieldname": "party_type",
"fieldtype": "Link",
"in_list_view": 1,
@@ -272,7 +271,7 @@
],
"idx": 1,
"istable": 1,
"modified": "2019-10-02 12:23:21.693443",
"modified": "2020-01-13 12:41:33.968025",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

View File

@@ -652,14 +652,16 @@ frappe.ui.form.on('Payment Entry', {
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
) {
if(total_positive_outstanding > total_negative_outstanding)
frm.set_value("paid_amount",
total_positive_outstanding - total_negative_outstanding);
if (!frm.doc.paid_amount)
frm.set_value("paid_amount",
total_positive_outstanding - total_negative_outstanding);
} else if (
total_negative_outstanding &&
total_positive_outstanding < total_negative_outstanding
) {
frm.set_value("received_amount",
total_negative_outstanding - total_positive_outstanding);
if (!frm.doc.received_amount)
frm.set_value("received_amount",
total_negative_outstanding - total_positive_outstanding);
}
}

View File

@@ -911,7 +911,10 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
else:
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
if dt not in ("Sales Invoice", "Purchase Invoice"):
party_account_currency = get_account_currency(party_account)
else:
party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
# payment type
if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \

View File

@@ -23,6 +23,8 @@ class PaymentReconciliation(Document):
if self.party_type in ["Customer", "Supplier"]:
dr_or_cr_notes = self.get_dr_or_cr_notes()
else:
dr_or_cr_notes = []
self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes)

View File

@@ -2,6 +2,16 @@ cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account
cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
cur_frm.add_fetch("payment_gateway_account", "message", "message")
frappe.ui.form.on("Payment Request", {
setup: function(frm) {
frm.set_query("party_type", function() {
return {
query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
};
});
}
})
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
if (frm.doc.reference_doctype) {
frappe.call({

View File

@@ -34,6 +34,9 @@ class PricingRule(Document):
def validate_duplicate_apply_on(self):
field = apply_on_dict.get(self.apply_on)
if not field:
return False
values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field)]
if len(values) != len(set(values)):
@@ -419,4 +422,4 @@ def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
return frappe.get_all('UOM Conversion Detail',
filters = {'parent': ('in', items), 'uom': ("like", "{0}%".format(txt))},
fields = ["distinct uom"], as_list=1)
fields = ["distinct uom"], as_list=1)

View File

@@ -495,7 +495,7 @@ def get_pricing_rule_items(pr_doc):
if pr_doc.apply_rule_on_other:
apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
apply_on_data.append(pr_doc.get(apply_on))
apply_on_data.append(pr_doc.get("other_" + apply_on))
return list(set(apply_on_data))

View File

@@ -382,21 +382,11 @@ cur_frm.fields_dict['items'].grid.get_field("item_code").get_query = function(do
cur_frm.fields_dict['credit_to'].get_query = function(doc) {
// filter on Account
if (doc.supplier) {
return {
filters: {
'account_type': 'Payable',
'is_group': 0,
'company': doc.company
}
}
} else {
return {
filters: {
'report_type': 'Balance Sheet',
'is_group': 0,
'company': doc.company
}
return {
filters: {
'account_type': 'Payable',
'is_group': 0,
'company': doc.company
}
}
}

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-21 16:16:39",
@@ -417,6 +418,7 @@
"fieldname": "contact_email",
"fieldtype": "Small Text",
"label": "Contact Email",
"options": "Email",
"print_hide": 1,
"read_only": 1
},
@@ -705,7 +707,7 @@
},
{
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"label": "Taxes and Charges Calculation",
"no_copy": 1,
"oldfieldtype": "HTML",
@@ -1287,7 +1289,8 @@
"icon": "fa fa-file-text",
"idx": 204,
"is_submittable": 1,
"modified": "2019-09-17 22:31:42.666601",
"links": [],
"modified": "2019-12-30 19:13:49.610538",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -556,22 +556,11 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn) {
}
cur_frm.set_query("debit_to", function(doc) {
// filter on Account
if (doc.customer) {
return {
filters: {
'account_type': 'Receivable',
'is_group': 0,
'company': doc.company
}
}
} else {
return {
filters: {
'report_type': 'Balance Sheet',
'is_group': 0,
'company': doc.company
}
return {
filters: {
'account_type': 'Receivable',
'is_group': 0,
'company': doc.company
}
}
});

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-24 19:29:05",
@@ -774,7 +775,7 @@
},
{
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"label": "Taxes and Charges Calculation",
"no_copy": 1,
"oldfieldtype": "HTML",
@@ -1567,7 +1568,8 @@
"icon": "fa fa-file-text",
"idx": 181,
"is_submittable": 1,
"modified": "2019-10-05 21:39:49.235990",
"links": [],
"modified": "2019-12-30 19:15:59.580414",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
import unittest, copy, time
from frappe.utils import nowdate, flt, getdate, cint
from frappe.utils import nowdate, flt, getdate, cint, add_days
from frappe.model.dynamic_links import get_dynamic_link_map
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
@@ -1847,6 +1847,26 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
def test_item_tax_validity(self):
item = frappe.get_doc("Item", "_Test Item 2")
if item.taxes:
item.taxes = []
item.save()
item.append("taxes", {
"item_tax_template": "_Test Item Tax Template 1",
"valid_from": add_days(nowdate(), 1)
})
item.save()
sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1)
sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1"
self.assertRaises(frappe.ValidationError, sales_invoice.save)
item.taxes = []
item.save()
def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice")

View File

@@ -188,7 +188,7 @@
}
],
"is_submittable": 1,
"modified": "2019-11-07 13:31:17.999744",
"modified": "2019-12-20 14:48:01.990600",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Share Transfer",
@@ -196,6 +196,7 @@
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -221,6 +222,7 @@
"write": 1
},
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -230,6 +232,7 @@
"report": 1,
"role": "Accounts Manager",
"share": 1,
"submit": 1,
"write": 1
}
],

View File

@@ -338,6 +338,16 @@ class Subscription(Document):
# Check invoice dates and make sure it doesn't have outstanding invoices
return getdate(nowdate()) >= getdate(self.current_invoice_start) and not self.has_outstanding_invoice()
def is_current_invoice_paid(self):
if self.is_new_subscription():
return False
last_invoice = frappe.get_doc('Sales Invoice', self.invoices[-1].invoice)
if getdate(last_invoice.posting_date) == getdate(self.current_invoice_start) and last_invoice.status == 'Paid':
return True
return False
def process_for_active(self):
"""
@@ -348,7 +358,7 @@ class Subscription(Document):
2. Change the `Subscription` status to 'Past Due Date'
3. Change the `Subscription` status to 'Cancelled'
"""
if self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice():
if not self.is_current_invoice_paid() and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
self.generate_invoice()
if self.current_invoice_is_past_due():
self.status = 'Past Due Date'

View File

@@ -1,134 +1,134 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:title",
"beta": 0,
"creation": "2018-11-22 23:38:39.668804",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:title",
"beta": 0,
"creation": "2018-11-22 23:38:39.668804",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title",
"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": "Title",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title",
"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": "Title",
"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": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-11-22 23:38:39.668804",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Category",
"name_case": "",
"owner": "Administrator",
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2020-01-15 17:14:28.951793",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Category",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}
}

View File

@@ -49,7 +49,7 @@
{% endfor %}
<tr>
<td class="right" colspan="3" ><strong>Total (debit) </strong></td>
<td class="left" >{{ gl | sum(attribute='debit') }}</td>
<td class="left" >{{ frappe.format((gl | sum(attribute="debit")), {fieldtype: "Currency"}) }}</td>
</tr>
<tr>
<td class="top-bottom" colspan="5"><strong>Credit</strong></td>
@@ -69,7 +69,7 @@
{% endfor %}
<tr>
<td class="right" colspan="3"><strong>Total (credit) </strong></td>
<td class="left" >{{ gl | sum(attribute='credit') }}</td>
<td class="left" >{{ frappe.format((gl | sum(attribute="credit")), {fieldtype: "Currency"}) }}</td>
</tr>
</table>
<div>

View File

@@ -171,7 +171,7 @@ class ReceivablePayableReport(object):
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
row.invoice_grand_total = row.invoiced
if abs(row.outstanding) > 0.1/10 ** self.currency_precision:
if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
# non-zero oustanding, we must consider this row
if self.is_invoice(row) and self.filters.based_on_payment_terms:
@@ -285,7 +285,7 @@ class ReceivablePayableReport(object):
def set_party_details(self, row):
# customer / supplier name
party_details = self.get_party_details(row.party)
party_details = self.get_party_details(row.party) or {}
row.update(party_details)
if self.filters.get(scrub(self.filters.party_type)):
row.currency = row.account_currency

View File

@@ -46,13 +46,24 @@ frappe.query_reports["Budget Variance Report"] = {
fieldtype: "Select",
options: ["Cost Center", "Project"],
default: "Cost Center",
reqd: 1
reqd: 1,
on_change: function() {
frappe.query_report.set_filter_value("budget_against_filter", []);
frappe.query_report.refresh();
}
},
{
fieldname: "cost_center",
label: __("Cost Center"),
fieldtype: "Link",
options: "Cost Center"
fieldname:"budget_against_filter",
label: __('Dimension Filter'),
fieldtype: "MultiSelectList",
get_data: function(txt) {
if (!frappe.query_report.filters) return;
let budget_against = frappe.query_report.get_filter_value('budget_against');
if (!budget_against) return;
return frappe.db.get_link_options(budget_against, txt);
}
},
{
fieldname:"show_cumulative",

View File

@@ -12,22 +12,22 @@ from six import iteritems
from pprint import pprint
def execute(filters=None):
if not filters: filters = {}
validate_filters(filters)
columns = get_columns(filters)
if filters.get("cost_center"):
cost_centers = [filters.get("cost_center")]
if filters.get("budget_against_filter"):
dimensions = filters.get("budget_against_filter")
else:
cost_centers = get_cost_centers(filters)
dimensions = get_cost_centers(filters)
period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"])
cam_map = get_cost_center_account_month_map(filters)
cam_map = get_dimension_account_month_map(filters)
data = []
for cost_center in cost_centers:
cost_center_items = cam_map.get(cost_center)
if cost_center_items:
for account, monthwise_data in iteritems(cost_center_items):
row = [cost_center, account]
for dimension in dimensions:
dimension_items = cam_map.get(dimension)
if dimension_items:
for account, monthwise_data in iteritems(dimension_items):
row = [dimension, account]
totals = [0, 0, 0]
for year in get_fiscal_years(filters):
last_total = 0
@@ -55,10 +55,6 @@ def execute(filters=None):
return columns, data
def validate_filters(filters):
if filters.get("budget_against") != "Cost Center" and filters.get("cost_center"):
frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center"))
def get_columns(filters):
columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"]
@@ -98,11 +94,12 @@ def get_cost_centers(filters):
else:
return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec
#Get cost center & target details
def get_cost_center_target_details(filters):
#Get dimension & target details
def get_dimension_target_details(filters):
cond = ""
if filters.get("cost_center"):
cond += " and b.cost_center=%s" % frappe.db.escape(filters.get("cost_center"))
if filters.get("budget_against_filter"):
cond += " and b.{budget_against} in (%s)".format(budget_against = \
frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter')))
return frappe.db.sql("""
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
@@ -110,8 +107,8 @@ def get_cost_center_target_details(filters):
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s
and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
(filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company), as_dict=True)
tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')),
as_dict=True)
#Get target distribution details of accounts of cost center
@@ -153,14 +150,14 @@ def get_actual_details(name, filters):
return cc_actual_details
def get_cost_center_account_month_map(filters):
def get_dimension_account_month_map(filters):
import datetime
cost_center_target_details = get_cost_center_target_details(filters)
dimension_target_details = get_dimension_target_details(filters)
tdd = get_target_distribution_details(filters)
cam_map = {}
for ccd in cost_center_target_details:
for ccd in dimension_target_details:
actual_details = get_actual_details(ccd.budget_against, filters)
for month_id in range(1, 13):

View File

@@ -33,7 +33,7 @@
</tr>
</thead>
<tbody>
{% for(var i=0, l=data.length-1; i<l; i++) { %}
{% for(var i=0, l=data.length; i<l; i++) { %}
<tr>
{% if(data[i].posting_date) { %}
<td>{%= frappe.datetime.str_to_user(data[i].posting_date) %}</td>

View File

@@ -38,32 +38,46 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
cost_center = list(set(invoice_cc_wh_map.get(inv.name, {}).get("cost_center", [])))
warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
row = [
inv.name, inv.posting_date, inv.customer, inv.customer_name
]
row = {
'invoice': inv.name,
'posting_date': inv.posting_date,
'customer': inv.customer,
'customer_name': inv.customer_name
}
if additional_query_columns:
for col in additional_query_columns:
row.append(inv.get(col))
row.update({
col: inv.get(col)
})
row.update({
'customer_group': inv.get("customer_group"),
'territory': inv.get("territory"),
'tax_id': inv.get("tax_id"),
'receivable_account': inv.debit_to,
'mode_of_payment': ", ".join(mode_of_payments.get(inv.name, [])),
'project': inv.project,
'owner': inv.owner,
'remarks': inv.remarks,
'sales_order': ", ".join(sales_order),
'delivery_note': ", ".join(delivery_note),
'cost_center': ", ".join(cost_center),
'warehouse': ", ".join(warehouse),
'currency': company_currency
})
row +=[
inv.get("customer_group"),
inv.get("territory"),
inv.get("tax_id"),
inv.debit_to, ", ".join(mode_of_payments.get(inv.name, [])),
inv.project, inv.owner, inv.remarks,
", ".join(sales_order), ", ".join(delivery_note),", ".join(cost_center),
", ".join(warehouse), company_currency
]
# map income values
base_net_total = 0
for income_acc in income_accounts:
income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
base_net_total += income_amount
row.append(income_amount)
row.update({
frappe.scrub(income_acc): income_amount
})
# net total
row.append(base_net_total or inv.base_net_total)
row.update({'net_total': base_net_total or inv.base_net_total})
# tax account
total_tax = 0
@@ -72,10 +86,18 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
total_tax += tax_amount
row.append(tax_amount)
row.update({
frappe.scrub(tax_acc): tax_amount
})
# total tax, grand total, outstanding amount & rounded total
row += [total_tax, inv.base_grand_total, inv.base_rounded_total, inv.outstanding_amount]
row.update({
'tax_total': total_tax,
'grand_total': inv.base_grand_total,
'rounded_total': inv.base_rounded_total,
'outstanding_amount': inv.outstanding_amount
})
data.append(row)
@@ -84,19 +106,118 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
def get_columns(invoice_list, additional_table_columns):
"""return columns based on filters"""
columns = [
_("Invoice") + ":Link/Sales Invoice:120", _("Posting Date") + ":Date:80",
_("Customer") + ":Link/Customer:120", _("Customer Name") + "::120"
{
'label': _("Invoice"),
'fieldname': 'invoice',
'fieldtype': 'Link',
'options': 'Sales Invoice',
'width': 120
},
{
'label': _("Posting Date"),
'fieldname': 'posting_date',
'fieldtype': 'Date',
'width': 80
},
{
'label': _("Customer"),
'fieldname': 'customer',
'fieldtype': 'Link',
'options': 'Customer',
'width': 120
},
{
'label': _("Customer Name"),
'fieldname': 'customer_name',
'fieldtype': 'Data',
'width': 120
},
]
if additional_table_columns:
columns += additional_table_columns
columns +=[
_("Customer Group") + ":Link/Customer Group:120", _("Territory") + ":Link/Territory:80",
_("Tax Id") + "::80", _("Receivable Account") + ":Link/Account:120", _("Mode of Payment") + "::120",
_("Project") +":Link/Project:80", _("Owner") + "::150", _("Remarks") + "::150",
_("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100",
_("Cost Center") + ":Link/Cost Center:100", _("Warehouse") + ":Link/Warehouse:100",
{
'label': _("Custmer Group"),
'fieldname': 'customer_group',
'fieldtype': 'Link',
'options': 'Customer Group',
'width': 120
},
{
'label': _("Territory"),
'fieldname': 'territory',
'fieldtype': 'Link',
'options': 'Territory',
'width': 80
},
{
'label': _("Tax Id"),
'fieldname': 'tax_id',
'fieldtype': 'Data',
'width': 120
},
{
'label': _("Receivable Account"),
'fieldname': 'receivable_account',
'fieldtype': 'Link',
'options': 'Account',
'width': 80
},
{
'label': _("Mode Of Payment"),
'fieldname': 'mode_of_payment',
'fieldtype': 'Data',
'width': 120
},
{
'label': _("Project"),
'fieldname': 'project',
'fieldtype': 'Link',
'options': 'project',
'width': 80
},
{
'label': _("Owner"),
'fieldname': 'owner',
'fieldtype': 'Data',
'width': 150
},
{
'label': _("Remarks"),
'fieldname': 'remarks',
'fieldtype': 'Data',
'width': 150
},
{
'label': _("Sales Order"),
'fieldname': 'sales_order',
'fieldtype': 'Link',
'options': 'Sales Order',
'width': 100
},
{
'label': _("Delivery Note"),
'fieldname': 'delivery_note',
'fieldtype': 'Link',
'options': 'Delivery Note',
'width': 100
},
{
'label': _("Cost Center"),
'fieldname': 'cost_center',
'fieldtype': 'Link',
'options': 'Cost Center',
'width': 100
},
{
'label': _("Warehouse"),
'fieldname': 'warehouse',
'fieldtype': 'Link',
'options': 'Warehouse',
'width': 100
},
{
"fieldname": "currency",
"label": _("Currency"),
@@ -105,7 +226,10 @@ def get_columns(invoice_list, additional_table_columns):
}
]
income_accounts = tax_accounts = income_columns = tax_columns = []
income_accounts = []
tax_accounts = []
income_columns = []
tax_columns = []
if invoice_list:
income_accounts = frappe.db.sql_list("""select distinct income_account
@@ -119,14 +243,65 @@ def get_columns(invoice_list, additional_table_columns):
and parent in (%s) order by account_head""" %
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
income_columns = [(account + ":Currency/currency:120") for account in income_accounts]
for account in income_accounts:
income_columns.append({
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
"options": 'currency',
"width": 120
})
for account in tax_accounts:
if account not in income_accounts:
tax_columns.append(account + ":Currency/currency:120")
tax_columns.append({
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
"options": 'currency',
"width": 120
})
columns = columns + income_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \
[_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
net_total_column = [{
"label": _("Net Total"),
"fieldname": "net_total",
"fieldtype": "Currency",
"options": 'currency',
"width": 120
}]
total_columns = [
{
"label": _("Tax Total"),
"fieldname": "tax_total",
"fieldtype": "Currency",
"options": 'currency',
"width": 120
},
{
"label": _("Grand Total"),
"fieldname": "grand_total",
"fieldtype": "Currency",
"options": 'currency',
"width": 120
},
{
"label": _("Rounded Total"),
"fieldname": "rounded_total",
"fieldtype": "Currency",
"options": 'currency',
"width": 120
},
{
"label": _("Outstanding Amount"),
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"options": 'currency',
"width": 120
}
]
columns = columns + income_columns + net_total_column + tax_columns + total_columns
return columns, income_accounts, tax_accounts

View File

@@ -65,6 +65,21 @@ frappe.query_reports["Trial Balance for Party"] = {
return party_type;
}
},
{
"fieldname": "account",
"label": __("Account"),
"fieldtype": "Link",
"options": "Account",
"get_query": function() {
var company = frappe.query_report.get_filter_value('company');
return {
"doctype": "Account",
"filters": {
"company": company,
}
}
}
},
{
"fieldname": "show_zero_values",
"label": __("Show zero values"),

View File

@@ -20,7 +20,7 @@ def execute(filters=None):
def get_data(filters, show_party_name):
if filters.get('party_type') in ('Customer', 'Supplier', 'Employee', 'Member'):
party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
if filters.get('party_type') == 'Student':
elif filters.get('party_type') == 'Student':
party_name_field = 'first_name'
elif filters.get('party_type') == 'Shareholder':
party_name_field = 'title'
@@ -96,13 +96,19 @@ def get_data(filters, show_party_name):
return data
def get_opening_balances(filters):
account_filter = ''
if filters.get('account'):
account_filter = "and account = %s" % (frappe.db.escape(filters.get('account')))
gle = frappe.db.sql("""
select party, sum(debit) as opening_debit, sum(credit) as opening_credit
from `tabGL Entry`
where company=%(company)s
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
group by party""", {
{account_filter}
group by party""".format(account_filter=account_filter), {
"company": filters.company,
"from_date": filters.from_date,
"party_type": filters.party_type
@@ -116,6 +122,11 @@ def get_opening_balances(filters):
return opening
def get_balances_within_period(filters):
account_filter = ''
if filters.get('account'):
account_filter = "and account = %s" % (frappe.db.escape(filters.get('account')))
gle = frappe.db.sql("""
select party, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry`
@@ -123,7 +134,8 @@ def get_balances_within_period(filters):
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
and ifnull(is_opening, 'No') = 'No'
group by party""", {
{account_filter}
group by party""".format(account_filter=account_filter), {
"company": filters.company,
"from_date": filters.from_date,
"to_date": filters.to_date,

View File

@@ -891,3 +891,9 @@ def get_allow_cost_center_in_entry_of_bs_account():
def generator():
return cint(frappe.db.get_value('Accounts Settings', None, 'allow_cost_center_in_entry_of_bs_account'))
return frappe.local_cache("get_allow_cost_center_in_entry_of_bs_account", (), generator, regenerate_if_none=True)
def get_stock_accounts(company):
return frappe.get_all("Account", filters = {
"account_type": "Stock",
"company": company
})

View File

@@ -144,6 +144,10 @@ frappe.ui.form.on('Asset', {
frm.set_df_property('purchase_invoice', 'read_only', 1);
frm.set_df_property('purchase_receipt', 'read_only', 1);
}
else if (frm.doc.is_existing_asset) {
frm.toggle_reqd('purchase_receipt', 0);
frm.toggle_reqd('purchase_invoice', 0);
}
else if (frm.doc.purchase_receipt) {
// if purchase receipt link is set then set PI disabled
frm.toggle_reqd('purchase_invoice', 0);
@@ -256,6 +260,7 @@ frappe.ui.form.on('Asset', {
},
is_existing_asset: function(frm) {
frm.trigger("toggle_reference_doc");
// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
},
@@ -333,25 +338,12 @@ frappe.ui.form.on('Asset', {
})
},
purchase_receipt: function(frm) {
purchase_receipt: (frm) => {
frm.trigger('toggle_reference_doc');
if (frm.doc.purchase_receipt) {
if (frm.doc.item_code) {
frappe.db.get_doc('Purchase Receipt', frm.doc.purchase_receipt).then(pr_doc => {
frm.set_value('company', pr_doc.company);
frm.set_value('purchase_date', pr_doc.posting_date);
const item = pr_doc.items.find(item => item.item_code === frm.doc.item_code);
if (!item) {
frm.set_value('purchase_receipt', '');
frappe.msgprint({
title: __('Invalid Purchase Receipt'),
message: __("The selected Purchase Receipt doesn't contains selected Asset Item."),
indicator: 'red'
});
}
frm.set_value('gross_purchase_amount', item.base_net_rate);
frm.set_value('location', item.asset_location);
frm.events.set_values_from_purchase_doc(frm, 'Purchase Receipt', pr_doc)
});
} else {
frm.set_value('purchase_receipt', '');
@@ -363,24 +355,12 @@ frappe.ui.form.on('Asset', {
}
},
purchase_invoice: function(frm) {
purchase_invoice: (frm) => {
frm.trigger('toggle_reference_doc');
if (frm.doc.purchase_invoice) {
if (frm.doc.item_code) {
frappe.db.get_doc('Purchase Invoice', frm.doc.purchase_invoice).then(pi_doc => {
frm.set_value('company', pi_doc.company);
frm.set_value('purchase_date', pi_doc.posting_date);
const item = pi_doc.items.find(item => item.item_code === frm.doc.item_code);
if (!item) {
frm.set_value('purchase_invoice', '');
frappe.msgprint({
title: __('Invalid Purchase Invoice'),
message: __("The selected Purchase Invoice doesn't contains selected Asset Item."),
indicator: 'red'
});
}
frm.set_value('gross_purchase_amount', item.base_net_rate);
frm.set_value('location', item.asset_location);
frm.events.set_values_from_purchase_doc(frm, 'Purchase Invoice', pi_doc)
});
} else {
frm.set_value('purchase_invoice', '');
@@ -392,6 +372,24 @@ frappe.ui.form.on('Asset', {
}
},
set_values_from_purchase_doc: function(frm, doctype, purchase_doc) {
frm.set_value('company', purchase_doc.company);
frm.set_value('purchase_date', purchase_doc.posting_date);
const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code);
if (!item) {
doctype_field = frappe.scrub(doctype)
frm.set_value(doctype_field, '');
frappe.msgprint({
title: __(`Invalid ${doctype}`),
message: __(`The selected ${doctype} doesn't contains selected Asset Item.`),
indicator: 'red'
});
}
frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount);
frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount);
frm.set_value('location', item.asset_location);
},
set_depreciation_rate: function(frm, row) {
if (row.total_number_of_depreciations && row.frequency_of_depreciation
&& row.expected_value_after_useful_life) {

View File

@@ -132,9 +132,10 @@ class Asset(AccountsController):
if len(movements) > 1:
frappe.throw(_('Asset has multiple Asset Movement Entries which has to be \
cancelled manually to cancel this asset.'))
movement = frappe.get_doc('Asset Movement', movements[0].get('name'))
movement.flags.ignore_validate = True
movement.cancel()
if movements:
movement = frappe.get_doc('Asset Movement', movements[0].get('name'))
movement.flags.ignore_validate = True
movement.cancel()
def make_asset_movement(self):
reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'

View File

@@ -110,7 +110,7 @@ class AssetMovement(Document):
ORDER BY
asm.transaction_date asc
""", (d.asset, self.company, 'Receipt'), as_dict=1)
if auto_gen_movement_entry[0].get('name') == self.name:
if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name:
frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
auto generated for Asset {1}').format(self.name, d.asset))

View File

@@ -122,7 +122,7 @@ def get_data(filters):
filters=conditions,
fields=["name", "asset_name", "department", "cost_center", "purchase_receipt",
"asset_category", "purchase_date", "gross_purchase_amount", "location",
"available_for_use_date", "status", "purchase_invoice"])
"available_for_use_date", "status", "purchase_invoice", "opening_accumulated_depreciation"])
for asset in assets_record:
asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \

View File

@@ -134,7 +134,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
if (doc.status != "On Hold") {
if(flt(doc.per_received, 2) < 100 && allow_receipt) {
cur_frm.add_custom_button(__('Receipt'), this.make_purchase_receipt, __('Create'));
if(doc.is_subcontracted==="Yes") {
if(doc.is_subcontracted==="Yes" && me.has_unsupplied_items()) {
cur_frm.add_custom_button(__('Material to Supplier'),
function() { me.make_stock_entry(); }, __("Transfer"));
}
@@ -191,6 +191,10 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
set_schedule_date(this.frm);
},
has_unsupplied_items: function() {
return this.frm.doc['supplied_items'].some(item => item.required_qty != item.supplied_qty)
},
make_stock_entry: function() {
var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; });
var me = this;
@@ -267,7 +271,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
if (me.frm.doc['supplied_items']) {
me.frm.doc['supplied_items'].forEach((item, index) => {
if (item.rm_item_code && item.main_item_code) {
if (item.rm_item_code && item.main_item_code && item.required_qty - item.supplied_qty != 0) {
me.raw_material_data.push ({
'name':item.name,
'item_code': item.main_item_code,

View File

@@ -56,6 +56,7 @@
"section_break_48",
"pricing_rules",
"raw_material_details",
"set_reserve_warehouse",
"supplied_items",
"sb_last_purchase",
"total_qty",
@@ -340,6 +341,7 @@
"fieldname": "contact_email",
"fieldtype": "Small Text",
"label": "Contact Email",
"options": "Email",
"print_hide": 1,
"read_only": 1
},
@@ -607,7 +609,7 @@
},
{
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"label": "Taxes and Charges Calculation",
"no_copy": 1,
"oldfieldtype": "HTML",
@@ -1039,12 +1041,19 @@
"fieldtype": "Link",
"label": "Tax Category",
"options": "Tax Category"
},
{
"depends_on": "supplied_items",
"fieldname": "set_reserve_warehouse",
"fieldtype": "Link",
"label": "Set Reserve Warehouse",
"options": "Warehouse"
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"modified": "2019-07-11 18:25:49.509343",
"modified": "2020-01-14 18:54:39.694448",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@@ -116,6 +116,73 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEqual(po.get("items")[0].amount, 1400)
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
def test_add_new_item_in_update_child_qty_rate(self):
po = create_purchase_order(do_not_save=1)
po.items[0].qty = 4
po.save()
po.submit()
pr = make_pr_against_po(po.name, 2)
po.load_from_db()
first_item_of_po = po.get("items")[0]
trans_item = json.dumps([
{
'item_code': first_item_of_po.item_code,
'rate': first_item_of_po.rate,
'qty': first_item_of_po.qty,
'docname': first_item_of_po.name
},
{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7}
])
update_child_qty_rate('Purchase Order', trans_item, po.name)
po.reload()
self.assertEquals(len(po.get('items')), 2)
self.assertEqual(po.status, 'To Receive and Bill')
def test_remove_item_in_update_child_qty_rate(self):
po = create_purchase_order(do_not_save=1)
po.items[0].qty = 4
po.save()
po.submit()
pr = make_pr_against_po(po.name, 2)
po.reload()
first_item_of_po = po.get("items")[0]
# add an item
trans_item = json.dumps([
{
'item_code': first_item_of_po.item_code,
'rate': first_item_of_po.rate,
'qty': first_item_of_po.qty,
'docname': first_item_of_po.name
},
{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7}])
update_child_qty_rate('Purchase Order', trans_item, po.name)
po.reload()
# check if can remove received item
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.get("items")[1].name}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Purchase Order', trans_item, po.name)
first_item_of_po = po.get("items")[0]
trans_item = json.dumps([
{
'item_code': first_item_of_po.item_code,
'rate': first_item_of_po.rate,
'qty': first_item_of_po.qty,
'docname': first_item_of_po.name
}
])
update_child_qty_rate('Purchase Order', trans_item, po.name)
po.reload()
self.assertEquals(len(po.get('items')), 1)
self.assertEqual(po.status, 'To Receive and Bill')
def test_update_qty(self):
po = create_purchase_order()

View File

@@ -138,7 +138,7 @@ def refresh_scorecards():
# Check to see if any new scorecard periods are created
if make_all_scorecards(sc.name) > 0:
# Save the scorecard to update the score and standings
sc.save()
frappe.get_doc('Supplier Scorecard', sc.name).save()
@frappe.whitelist()

View File

@@ -0,0 +1,89 @@
# Version 12.4.0 Release Note
### Accounts
- Validity of item tax. [#20135](https://github.com/frappe/erpnext/pull/20135)
- Dynamic filters for dimensions in the budget variance report. [#19973](https://github.com/frappe/erpnext/pull/19973)
- Purchase Receipt and Purchase Invoice is not mandatory for an existing asset. [19980](https://github.com/frappe/erpnext/pull/19980)
- Allowed multiple Landed Cost Vouchers against a Purchase Receipt / Purchase Invoice. [#20058](https://github.com/frappe/erpnext/pull/20058)
- Rounding adjustment while both inclusive tax and additional discount amount are applied. [#20078](https://github.com/frappe/erpnext/pull/20078)
- Fixed an error while doing payment reconciliation for party type Employee. [#20088](https://github.com/frappe/erpnext/pull/20088)
- Show Closing row in General Ledger print. [#20161](https://github.com/frappe/erpnext/pull/20161)
- New report - Stock and Account Balance Comparison. [#20226](https://github.com/frappe/erpnext/pull/20226)
- Paid amount should not be over-written on clicking "Get Outstanding Invoices" button in Payment Entry. [#20050](https://github.com/frappe/erpnext/pull/20050)
- Currency symbol in Sales / Purchase Register report
- Currency symbol in "Bank and Cash Payment Voucher" print format.
### Human Resource
- Fixed leave allocation on the compensatory leave request submission. [#19961](https://github.com/frappe/erpnext/pull/19961)
- Create Payment Entry against Employee Advance to return any unclaimed amount and update returned amount in Employee Advance. [#19955](https://github.com/frappe/erpnext/pull/19955)
- Set Party against loan accounts in an accrual journal entry for salary. [#20022](https://github.com/frappe/erpnext/pull/20022)
- Editable loan repayment schedule after submission [#20122](https://github.com/frappe/erpnext/pull/20112)
- Update the paid amount and status of a loan after processing salary slip against it. [#20023](https://github.com/frappe/erpnext/pull/20023)
- Settings to disable rounded total in salary slip via HR Settings. [#20150](https://github.com/frappe/erpnext/pull/20150)
- Submit Salary button was not showing after creating salary slip in payroll entry. [#19753](https://github.com/frappe/erpnext/pull/19753)
- Payment Entry against payroll entry should deduct loan amount (if there are any loan deductions in salary slip). [#20194](https://github.com/frappe/erpnext/pull/20194)
- Show only relevant "Job Offer" in Employee Onboarding based on Job Applicant
- Added dashboard in Employee Advance
### Manufacturing
- Fixed backflushed qty for partial receipt against a subcontracted purchase order. [#20026](https://github.com/frappe/erpnext/pull/20026)
- Added "Set Reserve Warehouse" field in sub-contracted Purchase Order. [19992](https://github.com/frappe/erpnext/pull/19992)
- Only shows if the supplied items table is not empty
- On entering a warehouse in the field, it sets / overwrites reserve warehouse in Supplied Raw Materials table.
- In Backflush Stock Entry against Work Order, additional cost for service items (defined in BOM) should come proportionately based on finished goods qty. [#20105](https://github.com/frappe/erpnext/pull/20105)
- Hide transfer button in a subcontracted PO if full qty is already transferred. [#20155](https://github.com/frappe/erpnext/pull/20155)
- Set correct valuation rate of finished goods item in case of multiple material consumptions. [#20165](https://github.com/frappe/erpnext/pull/20165)
- Job Card creation from Work Order dashboard
### Stock
- Fixed ambiguous column name in the Batch query. Test by searching in any Batch link field.
- Fixed incorrect reorder level in Stock balance report
- Validate Batch for serialized items
- Get the outgoing rate of serial no from SLE if serial no already transferred to another company. [#20171](https://github.com/frappe/erpnext/pull/20171)
- Deliver Note creation from Sales Order dashboard. [#20199](https://github.com/frappe/erpnext/pull/20199)
### Others
- Addition and deletion of items in submitted Sales Order / Purchase Order. [#19911](https://github.com/frappe/erpnext/pull/19911)
- Get item price based on price list considering minimum qty. [#20206](https://github.com/frappe/erpnext/pull/20206)
- Product Bundle item should not appear in dialog on click of "Create Material Request" button. [#20216](https://github.com/frappe/erpnext/pull/20216)
- Delete linked communications on the deletion of company transactions. [#19928](https://github.com/frappe/erpnext/pull/19928)

View File

@@ -336,6 +336,24 @@ def get_data():
"is_query_report": True,
"name": "Item Variant Details",
"doctype": "Item"
},
{
"type": "report",
"is_query_report": True,
"name": "Subcontracted Raw Materials To Be Transferred",
"doctype": "Purchase Order"
},
{
"type": "report",
"is_query_report": True,
"name": "Subcontracted Item To Be Received",
"doctype": "Purchase Order"
},
{
"type": "report",
"is_query_report": True,
"name": "Stock and Account Value Comparison",
"doctype": "Stock Ledger Entry"
}
]
},

View File

@@ -1155,6 +1155,30 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
child_item.base_amount = 1 # Initiallize value will update in parent validation
return child_item
def check_and_delete_children(parent, data):
deleted_children = []
updated_item_names = [d.get("docname") for d in data]
for item in parent.items:
if item.name not in updated_item_names:
deleted_children.append(item)
for d in deleted_children:
if parent.doctype == "Sales Order":
if flt(d.delivered_qty):
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been delivered").format(d.idx, d.item_code))
if flt(d.work_order_qty):
frappe.throw(_("Row #{0}: Cannot delete item {1} which has work order assigned to it.").format(d.idx, d.item_code))
if flt(d.ordered_qty):
frappe.throw(_("Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.").format(d.idx, d.item_code))
if parent.doctype == "Purchase Order" and flt(d.received_qty):
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code))
if flt(d.billed_amt):
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code))
d.cancel()
d.delete()
@frappe.whitelist()
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
@@ -1163,6 +1187,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
parent = frappe.get_doc(parent_doctype, parent_doctype_name)
check_and_delete_children(parent, data)
for d in data:
new_child_flag = False
if not d.get("docname"):

View File

@@ -4,9 +4,9 @@
from __future__ import unicode_literals
import frappe
from frappe.desk.reportview import get_match_cond, get_filters_cond
from frappe.utils import nowdate
from frappe.utils import nowdate, getdate
from collections import defaultdict
from erpnext.stock.get_item_details import _get_item_tax_template
# searches for active employees
def employee_query(doctype, txt, searchfield, start, page_len, filters):
@@ -311,6 +311,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
and sle.item_code = %(item_code)s
and sle.warehouse = %(warehouse)s
and (sle.batch_no like %(txt)s
or batch.expiry_date like %(txt)s
or batch.manufacturing_date like %(txt)s)
and batch.docstatus < 2
{cond}
@@ -329,6 +330,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
where batch.disabled = 0
and item = %(item_code)s
and (name like %(txt)s
or expiry_date like %(txt)s
or manufacturing_date like %(txt)s)
and docstatus < 2
{0}
@@ -484,7 +486,7 @@ def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters)
@frappe.whitelist()
def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters):
query = """
select pr.name
select pr.name
from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pritem
where pr.docstatus = 1 and pritem.parent = pr.name
and pr.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
@@ -497,7 +499,7 @@ def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
query = """
select pi.name
select pi.name
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` piitem
where pi.docstatus = 1 and piitem.parent = pi.name
and pi.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
@@ -506,3 +508,27 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
query += " and piitem.item_code = {item_code}".format(item_code = frappe.db.escape(filters.get('item_code')))
return frappe.db.sql(query, filters)
@frappe.whitelist()
def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
item_doc = frappe.get_cached_doc('Item', filters.get('item_code'))
item_group = filters.get('item_group')
taxes = item_doc.taxes or []
while item_group:
item_group_doc = frappe.get_cached_doc('Item Group', item_group)
taxes += item_group_doc.taxes or []
item_group = item_group_doc.parent_item_group
if not taxes:
return frappe.db.sql(""" SELECT name FROM `tabItem Tax Template` """)
else:
args = {
'item_code': filters.get('item_code'),
'posting_date': filters.get('valid_from'),
'tax_category': filters.get('tax_category')
}
taxes = _get_item_tax_template(args, taxes, for_validate=True)
return [(d,) for d in set(taxes)]

View File

@@ -20,6 +20,7 @@ class StockController(AccountsController):
def validate(self):
super(StockController, self).validate()
self.validate_inspection()
self.validate_serialized_batch()
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
if self.docstatus == 2:
@@ -42,6 +43,18 @@ class StockController(AccountsController):
gl_entries = self.get_asset_gl_entry(gl_entries)
make_gl_entries(gl_entries, from_repost=from_repost)
def validate_serialized_batch(self):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
for d in self.get("items"):
if not (hasattr(d, 'serial_no') and d.serial_no and d.batch_no): continue
serial_nos = get_serial_nos(d.serial_no)
for serial_no_data in frappe.get_all("Serial No",
filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]):
if serial_no_data.batch_no != d.batch_no:
frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
.format(d.idx, serial_no_data.name, d.batch_no))
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
default_cost_center=None):
@@ -54,6 +67,7 @@ class StockController(AccountsController):
gl_list = []
warehouse_with_no_account = []
precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
for item_row in voucher_details:
sle_list = sle_map.get(item_row.name)
if sle_list:
@@ -79,7 +93,7 @@ class StockController(AccountsController):
"against": item_row.expense_account,
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"debit": flt(sle.stock_value_difference, 2),
"debit": flt(sle.stock_value_difference, precision),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
@@ -89,7 +103,7 @@ class StockController(AccountsController):
"against": warehouse_account[sle.warehouse]["account"],
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"credit": flt(sle.stock_value_difference, 2),
"credit": flt(sle.stock_value_difference, precision),
"project": item_row.get("project") or self.get("project"),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
}, item=item_row))

View File

@@ -8,6 +8,7 @@ from frappe import _, scrub
from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
from erpnext.controllers.accounts_controller import validate_conversion_rate, \
validate_taxes_and_charges, validate_inclusive_tax
from erpnext.stock.get_item_details import _get_item_tax_template
class calculate_taxes_and_totals(object):
def __init__(self, doc):
@@ -34,6 +35,7 @@ class calculate_taxes_and_totals(object):
def _calculate(self):
self.validate_conversion_rate()
self.calculate_item_values()
self.validate_item_tax_template()
self.initialize_taxes()
self.determine_exclusive_rate()
self.calculate_net_total()
@@ -43,6 +45,38 @@ class calculate_taxes_and_totals(object):
self._cleanup()
self.calculate_total_net_weight()
def validate_item_tax_template(self):
for item in self.doc.get('items'):
if item.item_code and item.get('item_tax_template'):
item_doc = frappe.get_cached_doc("Item", item.item_code)
args = {
'tax_category': self.doc.get('tax_category'),
'posting_date': self.doc.get('posting_date'),
'bill_date': self.doc.get('bill_date'),
'transaction_date': self.doc.get('transaction_date')
}
item_group = item_doc.item_group
item_group_taxes = []
while item_group:
item_group_doc = frappe.get_cached_doc('Item Group', item_group)
item_group_taxes += item_group_doc.taxes or []
item_group = item_group_doc.parent_item_group
item_taxes = item_doc.taxes or []
if not item_group_taxes and (not item_taxes):
# No validation if no taxes in item or item group
continue
taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
if item.item_tax_template not in taxes:
frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format(
item.idx, frappe.bold(item.item_code)
))
def validate_conversion_rate(self):
# validate conversion rate
company_currency = erpnext.get_company_currency(self.doc.company)
@@ -312,11 +346,19 @@ class calculate_taxes_and_totals(object):
last_tax = self.doc.get("taxes")[-1]
non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
for d in self.doc.get("taxes") if not d.included_in_print_rate])
diff = self.doc.total + non_inclusive_tax_amount \
- flt(last_tax.total, last_tax.precision("total"))
# If discount amount applied, deduct the discount amount
# because self.doc.total is always without discount, but last_tax.total is after discount
if self.discount_amount_applied and self.doc.discount_amount:
diff -= flt(self.doc.discount_amount)
diff = flt(diff, self.doc.precision("rounding_adjustment"))
if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
self.doc.rounding_adjustment = flt(flt(self.doc.rounding_adjustment) +
flt(diff), self.doc.precision("rounding_adjustment"))
self.doc.rounding_adjustment = diff
def calculate_totals(self):
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \

View File

@@ -11,7 +11,7 @@ from datetime import timedelta
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import get_url
from frappe.utils import get_url, getdate
from frappe.utils.verified_command import verify_request, get_signed_params
@@ -117,7 +117,7 @@ class Appointment(Document):
if self._assign:
return
available_agents = _get_agents_sorted_by_asc_workload(
self.scheduled_time.date())
getdate(self.scheduled_time))
for agent in available_agents:
if(_check_agent_availability(agent, self.scheduled_time)):
agent = agent[0]
@@ -189,7 +189,7 @@ def _get_agents_sorted_by_asc_workload(date):
assigned_to = frappe.parse_json(appointment._assign)
if not assigned_to:
continue
if (assigned_to[0] in agent_list) and appointment.scheduled_time.date() == date:
if (assigned_to[0] in agent_list) and getdate(appointment.scheduled_time) == date:
appointment_counter[assigned_to[0]] += 1
sorted_agent_list = appointment_counter.most_common()
sorted_agent_list.reverse()

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"creation": "2018-10-25 10:02:48.656165",
"doctype": "DocType",
"editable_grid": 1,
@@ -23,48 +24,41 @@
},
{
"default": "0",
"depends_on": "eval:doc.enabled==1",
"depends_on": "enabled",
"fieldname": "automatic_sync",
"fieldtype": "Check",
"label": "Synchronize all accounts every hour"
},
{
"depends_on": "eval:doc.enabled==1",
"fieldname": "plaid_client_id",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Plaid Client ID",
"reqd": 1
"label": "Plaid Client ID"
},
{
"depends_on": "eval:doc.enabled==1",
"fieldname": "plaid_secret",
"fieldtype": "Password",
"in_list_view": 1,
"label": "Plaid Secret",
"reqd": 1
"label": "Plaid Secret"
},
{
"depends_on": "eval:doc.enabled==1",
"fieldname": "plaid_public_key",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Plaid Public Key",
"reqd": 1
"label": "Plaid Public Key"
},
{
"depends_on": "eval:doc.enabled==1",
"fieldname": "plaid_env",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Plaid Environment",
"reqd": 1
"label": "Plaid Environment"
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"depends_on": "enabled",
"fieldname": "section_break_4",
"fieldtype": "Section Break"
},
@@ -74,7 +68,8 @@
}
],
"issingle": 1,
"modified": "2019-08-13 17:00:06.939422",
"links": [],
"modified": "2020-01-05 10:00:22.137832",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Plaid Settings",

View File

@@ -34,7 +34,7 @@ frappe.ui.form.on('Employee Advance', {
}
else if (
frm.doc.docstatus === 1
&& flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount)
&& flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount)
&& frappe.model.can_create("Expense Claim")
) {
frm.add_custom_button(
@@ -45,6 +45,15 @@ frappe.ui.form.on('Employee Advance', {
__('Create')
);
}
if (frm.doc.docstatus === 1
&& (flt(frm.doc.claimed_amount) + flt(frm.doc.return_amount) < flt(frm.doc.paid_amount))
&& frappe.model.can_create("Journal Entry")) {
frm.add_custom_button(__("Return"), function() {
frm.trigger('make_return_entry');
}, __('Create'));
}
},
make_payment_entry: function(frm) {
@@ -83,6 +92,24 @@ frappe.ui.form.on('Employee Advance', {
});
},
make_return_entry: function(frm) {
frappe.call({
method: 'erpnext.hr.doctype.employee_advance.employee_advance.make_return_entry',
args: {
'employee': frm.doc.employee,
'company': frm.doc.company,
'employee_advance_name': frm.doc.name,
'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount),
'advance_account': frm.doc.advance_account,
'mode_of_payment': frm.doc.mode_of_payment
},
callback: function(r) {
const doclist = frappe.model.sync(r.message);
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
}
});
},
employee: function (frm) {
if (frm.doc.employee) {
return frappe.call({

View File

@@ -1,737 +1,213 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "naming_series:",
"beta": 0,
"creation": "2017-10-09 14:26:29.612365",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2017-10-09 14:26:29.612365",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"employee",
"employee_name",
"column_break_4",
"posting_date",
"department",
"section_break_8",
"purpose",
"column_break_11",
"advance_amount",
"paid_amount",
"due_advance_amount",
"claimed_amount",
"return_amount",
"section_break_7",
"status",
"company",
"amended_from",
"column_break_18",
"advance_account",
"mode_of_payment"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "naming_series",
"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",
"length": 0,
"no_copy": 0,
"options": "HR-EAD-.YYYY.-",
"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
},
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"options": "HR-EAD-.YYYY.-"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "employee",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Employee",
"length": 0,
"no_copy": 0,
"options": "Employee",
"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,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "employee",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Employee",
"options": "Employee",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Read Only",
"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": "Employee Name",
"length": 0,
"no_copy": 0,
"options": "",
"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
},
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Read Only",
"label": "Employee Name"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"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,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Posting Date",
"length": 0,
"no_copy": 0,
"options": "",
"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,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Posting Date",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.department",
"fieldname": "department",
"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": "Department",
"length": 0,
"no_copy": 0,
"options": "Department",
"permlevel": 0,
"precision": "",
"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
},
"fetch_from": "employee.department",
"fieldname": "department",
"fieldtype": "Link",
"label": "Department",
"options": "Department",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_8",
"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,
"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
},
"fieldname": "section_break_8",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "purpose",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Purpose",
"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,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "purpose",
"fieldtype": "Small Text",
"in_list_view": 1,
"label": "Purpose",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_11",
"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,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_11",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "advance_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Advance Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"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,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "advance_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Advance Amount",
"options": "Company:company:default_currency",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "paid_amount",
"fieldtype": "Currency",
"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": "Paid Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"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
},
"fieldname": "paid_amount",
"fieldtype": "Currency",
"label": "Paid Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:cur_frm.doc.employee",
"fieldname": "due_advance_amount",
"fieldtype": "Currency",
"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": "Due Advance Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"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
},
"depends_on": "eval:cur_frm.doc.employee",
"fieldname": "due_advance_amount",
"fieldtype": "Currency",
"label": "Due Advance Amount",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "claimed_amount",
"fieldtype": "Currency",
"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": "Claimed Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"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
},
"fieldname": "claimed_amount",
"fieldtype": "Currency",
"label": "Claimed Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_7",
"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,
"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
},
"fieldname": "section_break_7",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"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": "Status",
"length": 0,
"no_copy": 1,
"options": "Draft\nPaid\nUnpaid\nClaimed\nCancelled",
"permlevel": 0,
"precision": "",
"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
},
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"no_copy": 1,
"options": "Draft\nPaid\nUnpaid\nClaimed\nCancelled",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"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": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"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,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"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": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Employee Advance",
"permlevel": 0,
"print_hide": 1,
"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
},
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Employee Advance",
"print_hide": 1,
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_18",
"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,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_18",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "advance_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Advance Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "advance_account",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Advance Account",
"options": "Account",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "mode_of_payment",
"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": "Mode of Payment",
"length": 0,
"no_copy": 0,
"options": "Mode of Payment",
"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
"fieldname": "mode_of_payment",
"fieldtype": "Link",
"label": "Mode of Payment",
"options": "Mode of Payment"
},
{
"fieldname": "return_amount",
"fieldtype": "Currency",
"label": "Returned Amount",
"options": "Company:company:default_currency",
"read_only": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-01-30 11:28:15.529649",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Advance",
"name_case": "",
"owner": "Administrator",
],
"is_submittable": 1,
"links": [],
"modified": "2019-12-15 19:04:07.044505",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Advance",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Employee",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Employee",
"share": 1,
"write": 1
},
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Expense Approver",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Expense Approver",
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "employee,employee_name",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}
],
"search_fields": "employee,employee_name",
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -7,6 +7,7 @@ import frappe, erpnext
from frappe import _
from frappe.model.document import Document
from frappe.utils import flt, nowdate
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
class EmployeeAdvanceOverPayment(frappe.ValidationError):
pass
@@ -53,11 +54,25 @@ class EmployeeAdvance(Document):
and party = %s
""", (self.name, self.employee), as_dict=1)[0].paid_amount
return_amount = frappe.db.sql("""
select name, ifnull(sum(credit_in_account_currency), 0) as return_amount
from `tabGL Entry`
where against_voucher_type = 'Employee Advance'
and voucher_type != 'Expense Claim'
and against_voucher = %s
and party_type = 'Employee'
and party = %s
""", (self.name, self.employee), as_dict=1)[0].return_amount
if flt(paid_amount) > self.advance_amount:
frappe.throw(_("Row {0}# Paid Amount cannot be greater than requested advance amount"),
EmployeeAdvanceOverPayment)
if flt(return_amount) > self.paid_amount - self.claimed_amount:
frappe.throw(_("Return amount cannot be greater unclaimed amount"))
self.db_set("paid_amount", paid_amount)
self.db_set("return_amount", return_amount)
self.set_status()
frappe.db.set_value("Employee Advance", self.name , "status", self.status)
@@ -88,8 +103,6 @@ def get_due_advance_amount(employee, posting_date):
@frappe.whitelist()
def make_bank_entry(dt, dn):
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
doc = frappe.get_doc(dt, dn)
payment_account = get_default_bank_cash_account(doc.company, account_type="Cash",
mode_of_payment=doc.mode_of_payment)
@@ -118,3 +131,34 @@ def make_bank_entry(dt, dn):
})
return je.as_dict()
@frappe.whitelist()
def make_return_entry(employee, company, employee_advance_name,
return_amount, advance_account, mode_of_payment=None):
return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
je = frappe.new_doc('Journal Entry')
je.posting_date = nowdate()
je.voucher_type = 'Bank Entry'
je.company = company
je.remark = 'Return against Employee Advance: ' + employee_advance_name
je.append('accounts', {
'account': advance_account,
'credit_in_account_currency': return_amount,
'reference_type': 'Employee Advance',
'reference_name': employee_advance_name,
'party_type': 'Employee',
'party': employee,
'is_advance': 'Yes'
})
je.append("accounts", {
"account": return_account.account,
"debit_in_account_currency": return_amount,
"account_currency": return_account.account_currency,
"account_type": return_account.account_type
})
return je.as_dict()

View File

@@ -0,0 +1,19 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'employee_advance',
'non_standard_fieldnames': {
'Payment Entry': 'reference_name',
'Journal Entry': 'reference_name'
},
'transactions': [
{
'items': ['Expense Claim']
},
{
'items': ['Payment Entry', 'Journal Entry']
}
]
}

View File

@@ -7,6 +7,14 @@ frappe.ui.form.on('Employee Onboarding', {
frm.add_fetch("employee_onboarding_template", "department", "department");
frm.add_fetch("employee_onboarding_template", "designation", "designation");
frm.add_fetch("employee_onboarding_template", "employee_grade", "employee_grade");
frm.set_query('job_offer', function () {
return {
filters: {
'job_applicant': frm.doc.job_applicant
}
};
});
},
refresh: function(frm) {

View File

@@ -213,13 +213,14 @@ frappe.ui.form.on("Expense Claim", {
},
update_employee_advance_claimed_amount: function(frm) {
console.log("update_employee_advance_claimed_amount")
let amount_to_be_allocated = frm.doc.grand_total;
$.each(frm.doc.advances || [], function(i, advance){
if (amount_to_be_allocated >= advance.unclaimed_amount){
frm.doc.advances[i].allocated_amount = frm.doc.advances[i].unclaimed_amount;
advance.allocated_amount = frm.doc.advances[i].unclaimed_amount;
amount_to_be_allocated -= advance.allocated_amount;
} else{
frm.doc.advances[i].allocated_amount = amount_to_be_allocated;
advance.allocated_amount = amount_to_be_allocated;
amount_to_be_allocated = 0;
}
frm.refresh_field("advances");
@@ -295,6 +296,7 @@ frappe.ui.form.on("Expense Claim", {
doc: frm.doc,
callback: () => {
refresh_field("taxes");
frm.trigger("update_employee_advance_claimed_amount");
}
});
}
@@ -331,16 +333,12 @@ frappe.ui.form.on("Expense Claim", {
frappe.ui.form.on("Expense Claim Detail", {
amount: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
var doc = frm.doc;
frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount);
cur_frm.cscript.calculate_total(doc,cdt,cdn);
},
sanctioned_amount: function(frm, cdt, cdn) {
var doc = frm.doc;
cur_frm.cscript.calculate_total(doc,cdt,cdn);
cur_frm.cscript.calculate_total(frm.doc, cdt, cdn);
frm.trigger("get_taxes");
frm.trigger("calculate_grand_total");
}
});

View File

@@ -243,6 +243,7 @@ class ExpenseClaim(AccountsController):
precision = self.precision("total_advance_amount")
if flt(self.total_advance_amount, precision) > flt(self.total_claimed_amount, precision):
frappe.throw(_("Total advance amount cannot be greater than total claimed amount"))
if self.total_sanctioned_amount \
and flt(self.total_advance_amount, precision) > flt(self.total_sanctioned_amount, precision):
frappe.throw(_("Total advance amount cannot be greater than total sanctioned amount"))
@@ -321,7 +322,7 @@ def get_expense_claim_account(expense_claim_type, company):
@frappe.whitelist()
def get_advances(employee, advance_id=None):
if not advance_id:
condition = 'docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount'.format(frappe.db.escape(employee))
condition = 'docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount + return_amount'.format(frappe.db.escape(employee))
else:
condition = 'name={0}'.format(frappe.db.escape(advance_id))

View File

@@ -1,238 +1,93 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-10-09 16:53:26.410762",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"creation": "2017-10-09 16:53:26.410762",
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"employee_advance",
"posting_date",
"advance_paid",
"unclaimed_amount",
"allocated_amount",
"advance_account"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "employee_advance",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Employee Advance",
"length": 0,
"no_copy": 1,
"oldfieldname": "journal_voucher",
"oldfieldtype": "Link",
"options": "Employee Advance",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "250px",
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"columns": 2,
"fieldname": "employee_advance",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Employee Advance",
"no_copy": 1,
"oldfieldname": "journal_voucher",
"oldfieldtype": "Link",
"options": "Employee Advance",
"print_width": "250px",
"reqd": 1,
"width": "250px"
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Posting Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"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,
"unique": 0
},
"columns": 2,
"fieldname": "posting_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Posting Date",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "advance_paid",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Advance Paid",
"length": 0,
"no_copy": 0,
"options": "Company:company.default_currency",
"permlevel": 0,
"precision": "",
"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,
"unique": 0
},
"columns": 2,
"fieldname": "advance_paid",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Advance Paid",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "unclaimed_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Unclaimed amount",
"length": 0,
"no_copy": 1,
"oldfieldname": "advance_amount",
"oldfieldtype": "Currency",
"options": "Company:company.default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "120px",
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"columns": 2,
"fieldname": "unclaimed_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Unclaimed amount",
"no_copy": 1,
"oldfieldname": "advance_amount",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_width": "120px",
"read_only": 1,
"reqd": 1,
"width": "120px"
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "allocated_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Allocated amount",
"length": 0,
"no_copy": 1,
"oldfieldname": "allocated_amount",
"oldfieldtype": "Currency",
"options": "Company:company.default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "120px",
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"columns": 2,
"fieldname": "allocated_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Allocated amount",
"no_copy": 1,
"oldfieldname": "allocated_amount",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_width": "120px",
"width": "120px"
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "advance_account",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Advance Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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
"fieldname": "advance_account",
"fieldtype": "Link",
"hidden": 1,
"label": "Advance Account",
"options": "Account"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-10-09 19:59:48.818139",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Advance",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
],
"istable": 1,
"modified": "2020-01-14 17:49:17.968373",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Advance",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"creation": "2013-08-02 13:45:23",
"doctype": "DocType",
"document_type": "Other",
@@ -13,6 +14,7 @@
"expense_approver_mandatory_in_expense_claim",
"payroll_settings",
"include_holidays_in_total_working_days",
"disable_rounded_total",
"max_working_hours_against_timesheet",
"column_break_11",
"email_salary_slip_to_employee",
@@ -160,12 +162,20 @@
"fieldname": "auto_leave_encashment",
"fieldtype": "Check",
"label": "Auto Leave Encashment"
},
{
"default": "0",
"description": "If checked, hides and disables Rounded Total field in Salary Slips",
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
"label": "Disable Rounded Total"
}
],
"icon": "fa fa-cog",
"idx": 1,
"issingle": 1,
"modified": "2019-08-05 13:07:17.993968",
"links": [],
"modified": "2019-12-31 14:28:32.004121",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",

View File

@@ -7,6 +7,8 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import cint
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
class HRSettings(Document):
def validate(self):
@@ -22,3 +24,12 @@ class HRSettings(Document):
if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails:
if not self.password_policy:
frappe.throw(_("Password policy for Salary Slips is not set"))
def on_update(self):
self.toggle_rounded_total()
frappe.clear_cache()
def toggle_rounded_total(self):
self.disable_rounded_total = cint(self.disable_rounded_total)
make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check")
make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check")

View File

@@ -351,7 +351,7 @@ class LeaveApplication(Document):
pass
def create_leave_ledger_entry(self, submit=True):
if self.status != 'Approved':
if self.status != 'Approved' and submit:
return
expiry_date = get_allocation_expiry(self.employee, self.leave_type,

View File

@@ -235,8 +235,8 @@ class TestLeaveApplication(unittest.TestCase):
frappe.get_doc(dict(
doctype = 'Holiday List',
holiday_list_name = holiday_list,
from_date = date(date.today().year, 1, 1),
to_date = date(date.today().year, 12, 31),
from_date = add_months(today, -6),
to_date = add_months(today, 6),
holidays = [
dict(holiday_date = today, description = 'Test')
]
@@ -597,8 +597,8 @@ def get_leave_period():
return frappe.get_doc(dict(
name = 'Test Leave Period',
doctype = 'Leave Period',
from_date = "{0}-12-01".format(now_datetime().year - 1),
to_date = "{0}-12-31".format(now_datetime().year),
from_date = add_months(nowdate(), -6),
to_date = add_months(nowdate(), 6),
company = "_Test Company",
is_active = 1
)).insert()

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@ class Loan(AccountsController):
self.make_repayment_schedule()
self.set_repayment_period()
self.calculate_totals()
self.set_status(from_validate=True)
def set_missing_fields(self):
if not self.company:
@@ -95,43 +96,57 @@ class Loan(AccountsController):
self.total_payment = 0
self.total_interest_payable = 0
self.total_amount_paid = 0
for data in self.repayment_schedule:
self.total_payment += data.total_payment
self.total_interest_payable +=data.interest_amount
if data.paid:
self.total_amount_paid += data.total_payment
for schedule in self.repayment_schedule:
self.total_payment += schedule.total_payment
self.total_interest_payable +=schedule.interest_amount
if schedule.paid:
self.total_amount_paid += schedule.total_payment
def update_total_amount_paid(doc):
total_amount_paid = 0
for data in doc.repayment_schedule:
if data.paid:
total_amount_paid += data.total_payment
frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)
def update_total_amount_paid(self):
total_amount_paid = 0
for schedule in self.repayment_schedule:
if schedule.paid:
total_amount_paid += schedule.total_payment
frappe.db.set_value("Loan", self.name, "total_amount_paid", total_amount_paid)
def update_disbursement_status(doc):
disbursement = frappe.db.sql("""
select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
from `tabGL Entry`
where account = %s and against_voucher_type = 'Loan' and against_voucher = %s
""", (doc.payment_account, doc.name), as_dict=1)[0]
def set_status(self, from_validate=False):
disbursement = self.get_disbursement_entry()
disbursement_date = None
disbursement_date = None
if not disbursement or disbursement.disbursed_amount == 0:
status = "Sanctioned"
elif disbursement.disbursed_amount == doc.loan_amount:
disbursement_date = disbursement.posting_date
status = "Disbursed"
elif disbursement.disbursed_amount > doc.loan_amount:
frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount))
self.status = "Draft"
if status == 'Disbursed' and getdate(disbursement_date) > getdate(frappe.db.get_value("Loan", doc.name, "repayment_start_date")):
if (not disbursement or disbursement.disbursed_amount == 0) and self.docstatus == 1:
self.status = "Sanctioned"
if disbursement:
self.validate_disbursed_amount_and_loan_amount(disbursement.disbursed_amount)
if disbursement.disbursed_amount == self.loan_amount and disbursement.disbursed_amount != 0:
self.status = "Disbursed"
disbursement_date = disbursement.posting_date
self.validate_disbursement_date(disbursement_date, self.status)
if self.total_amount_paid == self.total_payment:
self.status = "Repaid/Closed"
if not from_validate:
frappe.db.set_value("Loan", self.name, "status", self.status)
if disbursement_date:
frappe.db.set_value("Loan", self.name, "disbursement_date", disbursement_date)
def validate_disbursement_date(self, disbursement_date, loan_status):
if loan_status == 'Disbursed' and getdate(disbursement_date) > getdate(frappe.db.get_value("Loan", self.name, "repayment_start_date")):
frappe.throw(_("Disbursement Date cannot be after Loan Repayment Start Date"))
frappe.db.sql("""
update `tabLoan`
set status = %s, disbursement_date = %s
where name = %s
""", (status, disbursement_date, doc.name))
def validate_disbursed_amount_and_loan_amount(self, disbursed_amount):
if disbursed_amount > self.loan_amount:
frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(self.loan_amount))
def get_disbursement_entry(self):
return frappe.db.sql("""
select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
from `tabGL Entry`
where account = %s and against_voucher_type = 'Loan' and against_voucher = %s
""", (self.payment_account, self.name), as_dict=1)[0]
def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods):
if repayment_method == "Repay Over Number of Periods" and not repayment_periods:

View File

@@ -31,7 +31,11 @@ frappe.ui.form.on('Payroll Entry', {
}
if ((frm.doc.employees || []).length) {
frm.page.set_primary_action(__('Create Salary Slips'), () => {
frm.save('Submit');
frm.save('Submit').then(()=>{
frm.page.clear_primary_action();
frm.refresh();
frm.events.refresh(frm);
});
});
}
}

View File

@@ -163,7 +163,7 @@ class PayrollEntry(Document):
"""
cond = self.get_filter_condition()
return frappe.db.sql(""" select eld.loan_account, eld.loan,
eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment
eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment,t1.employee
from
`tabSalary Slip` t1, `tabSalary Slip Loan` eld
where
@@ -246,6 +246,7 @@ class PayrollEntry(Document):
accounts.append({
"account": acc,
"debit_in_account_currency": flt(amount, precision),
"party_type": '',
"cost_center": self.cost_center,
"project": self.project
})
@@ -257,6 +258,7 @@ class PayrollEntry(Document):
"account": acc,
"credit_in_account_currency": flt(amount, precision),
"cost_center": self.cost_center,
"party_type": '',
"project": self.project
})
@@ -264,7 +266,9 @@ class PayrollEntry(Document):
for data in loan_details:
accounts.append({
"account": data.loan_account,
"credit_in_account_currency": data.principal_amount
"credit_in_account_currency": data.principal_amount,
"party_type": "Employee",
"party": data.employee
})
if data.interest_amount and not data.interest_income_account:
@@ -275,14 +279,17 @@ class PayrollEntry(Document):
"account": data.interest_income_account,
"credit_in_account_currency": data.interest_amount,
"cost_center": self.cost_center,
"project": self.project
"project": self.project,
"party_type": "Employee",
"party": data.employee
})
payable_amount -= flt(data.total_payment, precision)
# Payable amount
accounts.append({
"account": default_payroll_payable_account,
"credit_in_account_currency": flt(payable_amount, precision)
"credit_in_account_currency": flt(payable_amount, precision),
"party_type": '',
})
journal_entry.set("accounts", accounts)
@@ -322,6 +329,10 @@ class PayrollEntry(Document):
statistical_component = frappe.db.get_value("Salary Component", sal_detail.salary_component, 'statistical_component')
if statistical_component != 1:
salary_slip_total -= sal_detail.amount
#loan deduction from bank entry during payroll
salary_slip_total -= salary_slip.total_loan_repayment
if salary_slip_total > 0:
self.create_journal_entry(salary_slip_total, "salary")
@@ -546,7 +557,6 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr
count += 1
if publish_progress:
frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips..."))
if submitted_ss:
payroll_entry.make_accrual_jv_entry()
frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")

View File

@@ -1,231 +1,82 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2016-12-20 15:32:25.078334",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"creation": "2016-12-20 15:32:25.078334",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"payment_date",
"principal_amount",
"interest_amount",
"total_payment",
"balance_loan_amount",
"paid"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "payment_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Payment Date",
"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_on_submit": 1,
"columns": 2,
"fieldname": "payment_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Payment Date"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "principal_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Principal Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"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
},
"columns": 2,
"fieldname": "principal_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Principal Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "interest_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Interest Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"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
},
"columns": 2,
"fieldname": "interest_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Interest Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "total_payment",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Total Payment",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"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
},
"columns": 2,
"fieldname": "total_payment",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Total Payment",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "balance_loan_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Balance Loan Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"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
},
"columns": 2,
"fieldname": "balance_loan_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Balance Loan Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "paid",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Paid",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"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
"default": "0",
"fieldname": "paid",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Paid",
"read_only": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-03-30 17:37:31.834792",
"modified_by": "Administrator",
"module": "HR",
"name": "Repayment Schedule",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
],
"istable": 1,
"modified": "2019-10-29 11:45:10.694557",
"modified_by": "Administrator",
"module": "HR",
"name": "Repayment Schedule",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -50,7 +50,8 @@ class SalarySlip(TransactionBase):
self.calculate_net_pay()
company_currency = erpnext.get_company_currency(self.company)
self.total_in_words = money_in_words(self.rounded_total, company_currency)
total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total
self.total_in_words = money_in_words(total, company_currency)
if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"):
max_working_hours = frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet")
@@ -62,6 +63,7 @@ class SalarySlip(TransactionBase):
if self.net_pay < 0:
frappe.throw(_("Net Pay cannot be less than 0"))
else:
self.update_loans()
self.set_status()
self.update_status(self.name)
self.update_salary_slip_in_additional_salary()
@@ -69,6 +71,7 @@ class SalarySlip(TransactionBase):
self.email_salary_slip()
def on_cancel(self):
self.update_loans()
self.set_status()
self.update_status()
self.update_salary_slip_in_additional_salary()
@@ -90,6 +93,9 @@ class SalarySlip(TransactionBase):
if date_diff(self.end_date, self.start_date) < 0:
frappe.throw(_("To date cannot be before From date"))
def is_rounding_total_disabled(self):
return cint(frappe.db.get_single_value("HR Settings", "disable_rounded_total"))
def check_existing(self):
if not self.salary_slip_based_on_timesheet:
ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
@@ -764,7 +770,8 @@ class SalarySlip(TransactionBase):
self.total_principal_amount += loan.principal_amount
def get_loan_details(self):
return frappe.db.sql("""select rps.principal_amount, rps.interest_amount, l.name,
return frappe.db.sql("""select rps.principal_amount,
rps.name as repayment_name, rps.interest_amount, l.name,
rps.total_payment, l.loan_account, l.interest_income_account
from
`tabRepayment Schedule` as rps, `tabLoan` as l
@@ -815,6 +822,17 @@ class SalarySlip(TransactionBase):
timesheet.set_status()
timesheet.save()
def update_loans(self):
for loan in self.get_loan_details():
doc = frappe.get_doc("Loan", loan.name)
#setting repayment schedule and updating total amount to pay
repayment_status = 1 if doc.docstatus == 1 else 0
frappe.db.set_value("Repayment Schedule", loan.repayment_name, "paid", repayment_status)
doc.reload()
doc.update_total_amount_paid()
doc.set_status()
def set_status(self, status=None):
'''Get and update status'''
if not status:

File diff suppressed because it is too large Load Diff

View File

@@ -1,524 +1,170 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "HR-SSA-.YY.-.MM.-.#####",
"beta": 0,
"creation": "2018-04-13 16:38:41.769237",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_import": 1,
"autoname": "HR-SSA-.YY.-.MM.-.#####",
"creation": "2018-04-13 16:38:41.769237",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"employee",
"employee_name",
"department",
"designation",
"column_break_6",
"salary_structure",
"from_date",
"company",
"section_break_7",
"base",
"column_break_9",
"variable",
"amended_from"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "employee",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Employee",
"length": 0,
"no_copy": 0,
"options": "Employee",
"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,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "employee",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Employee",
"options": "Employee",
"reqd": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"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": "Employee Name",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"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
},
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Data",
"label": "Employee Name",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.department",
"fieldname": "department",
"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,
"label": "Department",
"length": 0,
"no_copy": 0,
"options": "Department",
"permlevel": 0,
"precision": "",
"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
},
"fetch_from": "employee.department",
"fieldname": "department",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Department",
"options": "Department",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.designation",
"fieldname": "designation",
"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,
"label": "Designation",
"length": 0,
"no_copy": 0,
"options": "Designation",
"permlevel": 0,
"precision": "",
"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
},
"fetch_from": "employee.designation",
"fieldname": "designation",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Designation",
"options": "Designation",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_6",
"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,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_6",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "salary_structure",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Salary Structure",
"length": 0,
"no_copy": 0,
"options": "Salary Structure",
"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,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "salary_structure",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Salary Structure",
"options": "Salary Structure",
"reqd": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"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": "From Date",
"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,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "from_date",
"fieldtype": "Date",
"label": "From Date",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"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": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"read_only": 1,
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_7",
"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,
"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
},
"fieldname": "section_break_7",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "base",
"fieldtype": "Currency",
"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": "Base",
"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
},
"fieldname": "base",
"fieldtype": "Currency",
"label": "Base",
"options": "Company:company:default_currency"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9",
"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,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "variable",
"fieldtype": "Currency",
"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": "Variable",
"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
},
"fieldname": "variable",
"fieldtype": "Currency",
"label": "Variable",
"options": "Company:company:default_currency"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"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": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Salary Structure Assignment",
"permlevel": 0,
"print_hide": 1,
"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
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Salary Structure Assignment",
"print_hide": 1,
"read_only": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 16:15:48.755450",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure Assignment",
"name_case": "",
"owner": "Administrator",
],
"is_submittable": 1,
"links": [],
"modified": "2019-12-31 17:05:28.637510",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure Assignment",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"share": 1,
"submit": 1,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "employee_name",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "employee_name",
"track_changes": 1
}

View File

@@ -633,7 +633,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
is_stock_item=is_stock_item,
qty_field="stock_qty",
select_columns = """, bom_item.source_warehouse, bom_item.operation,
bom_item.include_item_in_manufacturing, bom_item.description,
bom_item.include_item_in_manufacturing, bom_item.description, bom_item.rate,
(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s limit 1) as idx""")
items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True)
@@ -647,7 +647,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
qty_field="stock_qty" if fetch_qty_in_stock_uom else "qty",
select_columns = """, bom_item.uom, bom_item.conversion_factor, bom_item.source_warehouse,
bom_item.idx, bom_item.operation, bom_item.include_item_in_manufacturing,
bom_item.description """)
bom_item.description, bom_item.base_rate as rate """)
items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
for item in items:
@@ -760,23 +760,53 @@ def get_boms_in_bottom_up_order(bom_no=None):
def add_additional_cost(stock_entry, work_order):
# Add non stock items cost in the additional cost
bom = frappe.get_doc('BOM', work_order.bom_no)
table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items'
stock_entry.additional_costs = []
expenses_included_in_valuation = frappe.get_cached_value("Company", work_order.company,
"expenses_included_in_valuation")
add_non_stock_items_cost(stock_entry, work_order, expenses_included_in_valuation)
add_operations_cost(stock_entry, work_order, expenses_included_in_valuation)
def add_non_stock_items_cost(stock_entry, work_order, expense_account):
bom = frappe.get_doc('BOM', work_order.bom_no)
table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items'
items = {}
for d in bom.get(table):
items.setdefault(d.item_code, d.rate)
items.setdefault(d.item_code, d.amount)
non_stock_items = frappe.get_all('Item',
fields="name", filters={'name': ('in', list(items.keys())), 'ifnull(is_stock_item, 0)': 0}, as_list=1)
non_stock_items_cost = 0.0
for name in non_stock_items:
non_stock_items_cost += flt(items.get(name[0])) * flt(stock_entry.fg_completed_qty) / flt(bom.quantity)
stock_entry.append('additional_costs', {
'expense_account': expense_account,
'description': _("Non stock items"),
'amount': non_stock_items_cost
})
def add_operations_cost(stock_entry, work_order=None, expense_account=None):
from erpnext.stock.doctype.stock_entry.stock_entry import get_operating_cost_per_unit
operating_cost_per_unit = get_operating_cost_per_unit(work_order, stock_entry.bom_no)
if operating_cost_per_unit:
stock_entry.append('additional_costs', {
'expense_account': expenses_included_in_valuation,
'description': name[0],
'amount': items.get(name[0])
"expense_account": expense_account,
"description": _("Operating Cost as per Work Order / BOM"),
"amount": operating_cost_per_unit * flt(stock_entry.fg_completed_qty)
})
if work_order and work_order.additional_operating_cost and work_order.qty:
additional_operating_cost_per_unit = \
flt(work_order.additional_operating_cost) / flt(work_order.qty)
stock_entry.append('additional_costs', {
"expense_account": expense_account,
"description": "Additional Operating Cost",
"amount": additional_operating_cost_per_unit * flt(stock_entry.fg_completed_qty)
})
@frappe.whitelist()

View File

@@ -3,6 +3,11 @@
frappe.ui.form.on('Job Card', {
refresh: function(frm) {
if(frm.doc.docstatus == 0) {
frm.set_df_property("operation", "read_only", frm.doc.operation_id ? 1 : 0);
}
if(!frm.doc.__islocal && frm.doc.items && frm.doc.items.length) {
if (frm.doc.for_quantity != frm.doc.transferred_qty) {
frm.add_custom_button(__("Material Request"), () => {

View File

@@ -6,6 +6,7 @@ frappe.ui.form.on("Work Order", {
frm.custom_make_buttons = {
'Stock Entry': 'Start',
'Pick List': 'Create Pick List',
'Job Card': 'Create Job Card',
};
// Set query for warehouses
@@ -136,7 +137,7 @@ frappe.ui.form.on("Work Order", {
if (frm.doc.docstatus === 1
&& frm.doc.operations && frm.doc.operations.length
&& frm.doc.qty != frm.doc.material_transferred_for_manufacturing) {
&& frm.doc.qty != frm.doc.produced_qty) {
const not_completed = frm.doc.operations.filter(d => {
if(d.status != 'Completed') {

View File

@@ -13,7 +13,6 @@ from dateutil.relativedelta import relativedelta
from erpnext.stock.doctype.item.item import validate_end_of_life
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
from erpnext.projects.doctype.timesheet.timesheet import OverlapError
from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
from frappe.utils.csvutils import getlink
@@ -671,10 +670,6 @@ def make_stock_entry(work_order_id, purpose, qty=None):
stock_entry.from_warehouse = wip_warehouse
stock_entry.to_warehouse = work_order.fg_warehouse
stock_entry.project = work_order.project
if purpose=="Manufacture":
additional_costs = get_additional_costs(work_order, fg_qty=stock_entry.fg_completed_qty,
company=work_order.company)
stock_entry.set("additional_costs", additional_costs)
stock_entry.set_stock_entry_type()
stock_entry.get_items()

View File

@@ -13,6 +13,7 @@ erpnext.patches.v4_0.apply_user_permissions
erpnext.patches.v4_0.move_warehouse_user_to_restrictions
erpnext.patches.v4_0.global_defaults_to_system_settings
erpnext.patches.v4_0.update_incharge_name_to_sales_person_in_maintenance_schedule
execute:frappe.reload_doc("HR", "doctype", "HR Settings") #2020-01-16
execute:frappe.reload_doc('stock', 'doctype', 'warehouse') # 2017-04-24
execute:frappe.reload_doc('accounts', 'doctype', 'sales_invoice') # 2016-08-31
execute:frappe.reload_doc('selling', 'doctype', 'sales_order') # 2014-01-29
@@ -513,7 +514,6 @@ erpnext.patches.v11_0.rename_employee_loan_to_loan
erpnext.patches.v11_0.move_leave_approvers_from_employee #13-06-2018
erpnext.patches.v11_0.update_department_lft_rgt
erpnext.patches.v11_0.add_default_email_template_for_leave
execute:frappe.reload_doc("HR", "doctype", "HR Settings")
erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018
erpnext.patches.v11_0.uom_conversion_data #30-06-2018
erpnext.patches.v10_0.taxes_issue_with_pos
@@ -586,6 +586,7 @@ erpnext.patches.v11_0.add_permissions_in_gst_settings
erpnext.patches.v11_1.setup_guardian_role
execute:frappe.delete_doc('DocType', 'Notification Control')
erpnext.patches.v12_0.set_gst_category
erpnext.patches.v12_0.update_gst_category
erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants
erpnext.patches.v12_0.set_task_status
erpnext.patches.v11_0.make_italian_localization_fields # 26-03-2019
@@ -644,7 +645,6 @@ erpnext.patches.v12_0.set_expense_account_in_landed_cost_voucher_taxes
erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings
erpnext.patches.v12_0.set_payment_entry_status
erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields
erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
erpnext.patches.v12_0.update_price_or_product_discount
erpnext.patches.v12_0.add_export_type_field_in_party_master

View File

@@ -49,21 +49,22 @@ def execute():
item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code)
# update the item tax table
item = frappe.get_doc("Item", item_code)
item.set("taxes", [])
item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""})
frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code)
for d in item.taxes:
d.db_insert()
if item_tax_template_name:
item = frappe.get_doc("Item", item_code)
item.set("taxes", [])
item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""})
for d in item.taxes:
d.db_insert()
doctypes = [
'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice',
'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice'
]
for dt in doctypes:
for d in frappe.db.sql("""select name, parenttype, parent, item_code, item_tax_rate from `tab{0} Item`
where ifnull(item_tax_rate, '') not in ('', '{{}}')
where ifnull(item_tax_rate, '') not in ('', '{{}}')
and item_tax_template is NULL""".format(dt), as_dict=1):
item_tax_map = json.loads(d.item_tax_rate)
item_tax_template_name = get_item_tax_template(item_tax_templates,
@@ -95,27 +96,35 @@ def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttyp
else:
parts = tax_type.strip().split(" - ")
account_name = " - ".join(parts[:-1])
company = get_company(parts[-1], parenttype, parent)
parent_account = frappe.db.get_value("Account",
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account")
filters = {
"account_name": account_name,
"company": company,
"account_type": "Tax",
"parent_account": parent_account
}
tax_type = frappe.db.get_value("Account", filters)
if not tax_type:
account = frappe.new_doc("Account")
account.update(filters)
account.insert()
tax_type = account.name
if not account_name:
tax_type = None
else:
company = get_company(parts[-1], parenttype, parent)
parent_account = frappe.db.get_value("Account",
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account")
if not parent_account:
parent_account = frappe.db.get_value("Account",
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company})
filters = {
"account_name": account_name,
"company": company,
"account_type": "Tax",
"parent_account": parent_account
}
tax_type = frappe.db.get_value("Account", filters)
if not tax_type:
account = frappe.new_doc("Account")
account.update(filters)
account.insert()
tax_type = account.name
item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
item_tax_templates.setdefault(item_tax_template.title, {})
item_tax_templates[item_tax_template.title][tax_type] = tax_rate
item_tax_template.save()
return item_tax_template.name
if tax_type:
item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
item_tax_templates.setdefault(item_tax_template.title, {})
item_tax_templates[item_tax_template.title][tax_type] = tax_rate
if item_tax_template.get("taxes"):
item_tax_template.save()
return item_tax_template.name
def get_company(company_abbr, parenttype=None, parent=None):
if parenttype and parent:

View File

@@ -1,5 +0,0 @@
import frappe
def execute():
frappe.db.set_value("Accounts Settings", None, "add_taxes_from_item_tax_template", 1)
frappe.db.set_default("add_taxes_from_item_tax_template", 1)

View File

@@ -0,0 +1,19 @@
from __future__ import unicode_literals
import frappe
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
frappe.db.sql(""" UPDATE `tabSales Invoice` set gst_category = 'Unregistered'
where gst_category = 'Registered Regular'
and ifnull(customer_gstin, '')=''
and ifnull(billing_address_gstin,'')=''
""")
frappe.db.sql(""" UPDATE `tabPurchase Invoice` set gst_category = 'Unregistered'
where gst_category = 'Registered Regular'
and ifnull(supplier_gstin, '')=''
""")

View File

@@ -13,6 +13,8 @@ def execute():
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration_category')
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission_detail')
frappe.reload_doc('accounts', 'doctype', 'tax_category')
for doctype in ["Sales Invoice", "Delivery Note", "Purchase Invoice"]:
frappe.db.sql("""delete from `tabCustom Field` where dt = %s
and fieldname in ('port_code', 'shipping_bill_number', 'shipping_bill_date')""", doctype)
@@ -29,4 +31,4 @@ def execute():
update tabAddress
set gst_state_number=concat("0", gst_state_number)
where ifnull(gst_state_number, '') != '' and gst_state_number<10
""")
""")

View File

@@ -107,6 +107,12 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
filters:{ 'item_code': row.item_code }
}
});
if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) {
return me.set_query_for_item_tax_template(doc, cdt, cdn)
});
}
},
refresh: function(doc) {

View File

@@ -388,9 +388,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
var diff = me.frm.doc.total + non_inclusive_tax_amount
- flt(last_tax.total, precision("grand_total"));
if(me.discount_amount_applied && me.frm.doc.discount_amount) {
diff -= flt(me.frm.doc.discount_amount);
}
diff = flt(diff, precision("rounding_adjustment"));
if ( diff && Math.abs(diff) <= (5.0 / Math.pow(10, precision("tax_amount", last_tax))) ) {
this.frm.doc.rounding_adjustment = flt(flt(this.frm.doc.rounding_adjustment) + diff,
precision("rounding_adjustment"));
me.frm.doc.rounding_adjustment = diff;
}
}
}

View File

@@ -968,7 +968,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
qty: function(doc, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
this.conversion_factor(doc, cdt, cdn, true);
this.conversion_factor(doc, cdt, cdn, false);
this.apply_pricing_rule(item, true);
},
@@ -1398,7 +1398,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
remove_pricing_rule: function(item) {
let me = this;
const fields = ["discount_percentage", "discount_amount", "pricing_rules"];
const fields = ["discount_percentage",
"discount_amount", "margin_rate_or_amount", "rate_with_margin"];
if(item.remove_free_item) {
var items = [];
@@ -1418,6 +1419,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
fields.forEach(f => {
row[f] = 0;
});
["pricing_rules", "margin_type"].forEach(field => {
if (row[field]) {
row[field] = '';
}
})
}
});
@@ -1693,6 +1700,29 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
},
set_query_for_item_tax_template: function(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
if(!item.item_code) {
frappe.throw(__("Please enter Item Code to get item taxes"));
} else {
let filters = {
'item_code': item.item_code,
'valid_from': doc.transaction_date || doc.bill_date || doc.posting_date,
'item_group': item.item_group,
}
if (doc.tax_category)
filters['tax_category'] = doc.tax_category;
return {
query: "erpnext.controllers.queries.get_tax_template",
filters: filters
}
}
},
payment_terms_template: function() {
var me = this;
const doc = this.frm.doc;
@@ -1767,14 +1797,28 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
},
set_reserve_warehouse: function() {
this.autofill_warehouse("reserve_warehouse");
},
set_warehouse: function() {
this.autofill_warehouse("warehouse");
},
autofill_warehouse : function (warehouse_field) {
// set warehouse in all child table rows
var me = this;
if(this.frm.doc.set_warehouse) {
$.each(this.frm.doc.items || [], function(i, item) {
frappe.model.set_value(me.frm.doctype + " Item", item.name, "warehouse", me.frm.doc.set_warehouse);
let warehouse = (warehouse_field === "warehouse") ? me.frm.doc.set_warehouse : me.frm.doc.set_reserve_warehouse;
let child_table = (warehouse_field === "warehouse") ? me.frm.doc.items : me.frm.doc.supplied_items;
let doctype = (warehouse_field === "warehouse") ? (me.frm.doctype + " Item") : (me.frm.doctype + " Item Supplied");
if(warehouse) {
$.each(child_table || [], function(i, item) {
frappe.model.set_value(doctype, item.name, warehouse_field, warehouse);
});
}
},
coupon_code: function() {
var me = this;
frappe.run_serially([

View File

@@ -458,7 +458,8 @@ erpnext.utils.update_child_items = function(opts) {
fieldname:"item_code",
options: 'Item',
in_list_view: 1,
read_only: 1,
read_only: 0,
disabled: 0,
label: __('Item Code')
}, {
fieldtype:'Float',
@@ -501,6 +502,7 @@ erpnext.utils.update_child_items = function(opts) {
frm.doc[opts.child_docname].forEach(d => {
dialog.fields_dict.trans_items.df.data.push({
"docname": d.name,
"name": d.name,
"item_code": d.item_code,
"qty": d.qty,
"rate": d.rate,

View File

@@ -139,7 +139,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
this.dialog.set_value('serial_no', d.serial_no);
}
if (d.batch_no) {
if (d.has_batch_no && d.batch_no) {
this.frm.doc.items.forEach(data => {
if(data.item_code == d.item_code) {
this.dialog.fields_dict.batches.df.data.push({

View File

@@ -3,6 +3,26 @@
frappe.ui.form.on('GST HSN Code', {
refresh: function(frm) {
if(! frm.doc.__islocal && frm.doc.taxes.length){
frm.add_custom_button(__('Update Taxes for Items'), function(){
frappe.confirm(
'Are you sure? It will overwrite taxes for all items with HSN Code <b>'+frm.doc.name+'</b>.',
function(){
frappe.call({
args:{
taxes: frm.doc.taxes,
hsn_code: frm.doc.name
},
method: 'erpnext.regional.doctype.gst_hsn_code.gst_hsn_code.update_taxes_in_item_master',
callback: function(r) {
if(r.message){
frappe.show_alert(__('Item taxes updated'));
}
}
});
}
);
});
}
}
});

View File

@@ -1,104 +1,48 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:hsn_code",
"beta": 0,
"creation": "2017-06-21 10:48:56.422086",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"autoname": "field:hsn_code",
"creation": "2017-06-21 10:48:56.422086",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"hsn_code",
"description",
"taxes"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "hsn_code",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "HSN Code",
"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,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "hsn_code",
"fieldtype": "Data",
"in_list_view": 1,
"label": "HSN Code",
"reqd": 1,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Description",
"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
"fieldname": "description",
"fieldtype": "Small Text",
"in_list_view": 1,
"label": "Description"
},
{
"fieldname": "taxes",
"fieldtype": "Table",
"label": "Taxes",
"options": "Item Tax"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-29 14:38:52.220743",
"modified_by": "Administrator",
"module": "Regional",
"name": "GST HSN Code",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "hsn_code, description",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "hsn_code",
"track_changes": 1,
"track_seen": 0
],
"links": [],
"modified": "2019-12-29 11:32:06.849631",
"modified_by": "Administrator",
"module": "Regional",
"name": "GST HSN Code",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"search_fields": "hsn_code, description",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "hsn_code",
"track_changes": 1
}

View File

@@ -8,3 +8,26 @@ from frappe.model.document import Document
class GSTHSNCode(Document):
pass
@frappe.whitelist()
def update_taxes_in_item_master(taxes, hsn_code):
items = frappe.get_list("Item", filters={
'gst_hsn_code': hsn_code
})
taxes = frappe.parse_json(taxes)
frappe.enqueue(update_item_document, items=items, taxes=taxes)
return 1
def update_item_document(items, taxes):
for item in items:
item_to_be_updated=frappe.get_doc("Item", item.name)
item_to_be_updated.taxes = []
for tax in taxes:
tax = frappe._dict(tax)
item_to_be_updated.append("taxes", {
'item_tax_template': tax.item_tax_template,
'tax_category': tax.tax_category,
'valid_from': tax.valid_from
})
item_to_be_updated.save()

View File

@@ -10,7 +10,9 @@ Provide a report and downloadable CSV according to the German DATEV format.
from __future__ import unicode_literals
import datetime
import json
import six
from six import string_types
import frappe
from frappe import _
import pandas as pd
@@ -158,7 +160,7 @@ def get_gl_entries(filters, as_dict):
where gl.company = %(company)s
and DATE(gl.posting_date) >= %(from_date)s
and DATE(gl.posting_date) <= %(to_date)s
order by 'Belegdatum', gl.voucher_no""", filters, as_dict=as_dict)
order by 'Belegdatum', gl.voucher_no""", filters, as_dict=as_dict, as_utf8=1)
return gl_entries
@@ -404,7 +406,8 @@ def get_datev_csv(data, filters):
header = ';'.join(header).encode('latin_1')
data = result.to_csv(
sep=b';',
# Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035
sep=str(';'),
# European decimal seperator
decimal=',',
# Windows "ANSI" encoding
@@ -412,13 +415,16 @@ def get_datev_csv(data, filters):
# format date as DDMM
date_format='%d%m',
# Windows line terminator
line_terminator=b'\r\n',
line_terminator='\r\n',
# Do not number rows
index=False,
# Use all columns defined above
columns=columns
)
if not six.PY2:
data = data.encode('latin_1')
return header + b'\r\n' + data
@frappe.whitelist()

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-24 19:29:08",
@@ -536,7 +537,7 @@
},
{
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"label": "Taxes and Charges Calculation",
"no_copy": 1,
"oldfieldtype": "HTML",
@@ -927,8 +928,9 @@
"icon": "fa fa-shopping-cart",
"idx": 82,
"is_submittable": 1,
"links": [],
"max_attachments": 1,
"modified": "2019-11-12 13:19:11.895715",
"modified": "2019-12-30 19:14:56.630270",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",

View File

@@ -6,13 +6,14 @@
frappe.ui.form.on("Sales Order", {
setup: function(frm) {
frm.custom_make_buttons = {
'Delivery Note': 'Delivery',
'Delivery Note': 'Delivery Note',
'Pick List': 'Pick List',
'Sales Invoice': 'Invoice',
'Material Request': 'Material Request',
'Purchase Order': 'Purchase Order',
'Project': 'Project',
'Payment Entry': "Payment"
'Payment Entry': "Payment",
'Work Order': "Work Order"
}
frm.add_fetch('customer', 'tax_id', 'tax_id');
@@ -134,7 +135,6 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
}
if(doc.status !== 'Closed') {
if(doc.status !== 'On Hold') {
allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty))
&& !this.frm.doc.skip_delivery_note
@@ -696,4 +696,4 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
});
}
});
$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));
$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-06-18 12:39:59",
@@ -619,7 +620,7 @@
},
{
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"label": "Taxes and Charges Calculation",
"no_copy": 1,
"oldfieldtype": "HTML",
@@ -1194,7 +1195,8 @@
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"modified": "2019-10-23 14:26:42.767189",
"links": [],
"modified": "2019-12-30 19:15:28.605085",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",

View File

@@ -380,6 +380,9 @@ class SalesOrder(SellingController):
def get_work_order_items(self, for_raw_material_request=0):
'''Returns items with BOM that already do not have a linked work order'''
items = []
item_codes = [i.item_code for i in self.items]
product_bundle_parents = [pb.new_item_code for pb in frappe.get_all("Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"])]
for table in [self.items, self.packed_items]:
for i in table:
bom = get_default_bom_item(i.item_code)
@@ -391,7 +394,7 @@ class SalesOrder(SellingController):
else:
pending_qty = stock_qty
if pending_qty:
if pending_qty and i.item_code not in product_bundle_parents:
if bom:
items.append(dict(
name= i.name,
@@ -420,7 +423,7 @@ class SalesOrder(SellingController):
def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date):
delivery_date = get_next_schedule_date(ref_doc_delivery_date,
auto_repeat_doc.frequency, cint(auto_repeat_doc.repeat_on_day))
auto_repeat_doc.frequency, auto_repeat_doc.start_date, cint(auto_repeat_doc.repeat_on_day))
if delivery_date <= transaction_date:
delivery_date_diff = frappe.utils.date_diff(ref_doc_delivery_date, red_doc_transaction_date)

View File

@@ -320,7 +320,12 @@ class TestSalesOrder(unittest.TestCase):
create_dn_against_so(so.name, 4)
make_sales_invoice(so.name)
trans_item = json.dumps([{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 7}])
first_item_of_so = so.get("items")[0]
trans_item = json.dumps([
{'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \
'qty' : first_item_of_so.qty, 'docname': first_item_of_so.name},
{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 7}
])
update_child_qty_rate('Sales Order', trans_item, so.name)
so.reload()
@@ -329,6 +334,48 @@ class TestSalesOrder(unittest.TestCase):
self.assertEqual(so.get("items")[-1].qty, 7)
self.assertEqual(so.get("items")[-1].amount, 1400)
self.assertEqual(so.status, 'To Deliver and Bill')
def test_remove_item_in_update_child_qty_rate(self):
so = make_sales_order(**{
"item_list": [{
"item_code": '_Test Item',
"qty": 5,
"rate":1000
}]
})
create_dn_against_so(so.name, 2)
make_sales_invoice(so.name)
# add an item so as to try removing items
trans_item = json.dumps([
{"item_code": '_Test Item', "qty": 5, "rate":1000, "docname": so.get("items")[0].name},
{"item_code": '_Test Item 2', "qty": 2, "rate":500}
])
update_child_qty_rate('Sales Order', trans_item, so.name)
so.reload()
self.assertEqual(len(so.get("items")), 2)
# check if delivered items can be removed
trans_item = json.dumps([{
"item_code": '_Test Item 2',
"qty": 2,
"rate":500,
"docname": so.get("items")[1].name
}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Sales Order', trans_item, so.name)
#remove last added item
trans_item = json.dumps([{
"item_code": '_Test Item',
"qty": 5,
"rate":1000,
"docname": so.get("items")[0].name
}])
update_child_qty_rate('Sales Order', trans_item, so.name)
so.reload()
self.assertEqual(len(so.get("items")), 1)
self.assertEqual(so.status, 'To Deliver and Bill')
def test_update_child_qty_rate(self):

View File

@@ -84,6 +84,13 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
return me.set_query_for_batch(doc, cdt, cdn)
});
}
if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) {
return me.set_query_for_item_tax_template(doc, cdt, cdn)
});
}
},
refresh: function() {

View File

@@ -76,7 +76,7 @@ class AuthorizationControl(TransactionBase):
add_cond = ''
auth_value = av_dis
if val == 1: add_cond += " and system_user = '"+session['user'].replace("'", "\\'")+"'"
if val == 1: add_cond += " and system_user = '"+ frappe.db.escape(session['user']) +"'"
elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.get_roles())+"')")
else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''"
@@ -85,7 +85,7 @@ class AuthorizationControl(TransactionBase):
if doc_obj:
if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer
else: customer = doc_obj.customer_name
add_cond = " and master_name = '"+cstr(customer).replace("'", "\\'")+"'"
add_cond = " and master_name = '"+ frappe.db.escape(customer) +"'"
if based_on == 'Itemwise Discount':
if doc_obj:
for t in doc_obj.get("items"):

View File

@@ -106,7 +106,11 @@ def delete_lead_addresses(company_name):
frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
def delete_communications(doctype, company_name, company_fieldname):
frappe.db.sql("""
DELETE FROM `tabCommunication` WHERE reference_doctype = %s AND
EXISTS (SELECT name FROM `tab{0}` WHERE {1} = %s AND `tabCommunication`.reference_name = name)
""".format(doctype, company_fieldname), (doctype, company_name))
refrence_docs = frappe.get_all(doctype, filters={company_fieldname:company_name})
reference_doctype_names = [r.name for r in refrence_docs]
communications = frappe.get_all("Communication", filters={"reference_doctype":doctype,"reference_name":["in",reference_doctype_names]})
communication_names = [c.name for c in communications]
frappe.delete_doc("Communication",communication_names)

View File

@@ -88,6 +88,56 @@ class TestCompany(unittest.TestCase):
self.delete_mode_of_payment(template)
frappe.delete_doc("Company", template)
def test_delete_communication(self):
from erpnext.setup.doctype.company.delete_company_transactions import delete_communications
company = create_child_company()
lead = create_test_lead_in_company(company)
communication = create_company_communication("Lead", lead)
delete_communications("Lead", "Test Company", "company")
self.assertFalse(frappe.db.exists("Communcation", communication))
self.assertFalse(frappe.db.exists({"doctype":"Comunication Link", "link_name": communication}))
def delete_mode_of_payment(self, company):
frappe.db.sql(""" delete from `tabMode of Payment Account`
where company =%s """, (company))
def create_company_communication(doctype, docname):
comm = frappe.get_doc({
"doctype": "Communication",
"communication_type": "Communication",
"content": "Deduplication of Links",
"communication_medium": "Email",
"reference_doctype":doctype,
"reference_name":docname
})
comm.insert()
def create_child_company():
child_company = frappe.db.exists("Company", "Test Company")
if not child_company:
child_company = frappe.get_doc({
"doctype":"Company",
"company_name":"Test Company",
"abbr":"test_company",
"default_currency":"INR"
})
child_company.insert()
else:
child_company = frappe.get_doc("Company", child_company)
return child_company.name
def create_test_lead_in_company(company):
lead = frappe.db.exists("Lead", "Test Lead in new company")
if not lead:
lead = frappe.get_doc({
"doctype": "Lead",
"lead_name": "Test Lead in new company",
"scompany": company
})
lead.insert()
else:
lead = frappe.get_doc("Lead", lead)
lead.company = company
lead.save()
return lead.name

View File

@@ -226,16 +226,14 @@ def set_batch_nos(doc, warehouse_field, throw=False):
warehouse = d.get(warehouse_field, None)
if has_batch_no and warehouse and qty > 0:
if not d.batch_no:
d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw)
d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw, d.serial_no)
else:
batch_qty = get_batch_qty(batch_no=d.batch_no, warehouse=warehouse)
if flt(batch_qty, d.precision("qty")) < flt(qty, d.precision("qty")):
frappe.throw(_("Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches").format(d.idx, d.batch_no, batch_qty, qty))
@frappe.whitelist()
def get_batch_no(item_code, warehouse, qty=1, throw=False):
def get_batch_no(item_code, warehouse, qty=1, throw=False, serial_no=None):
"""
Get batch number using First Expiring First Out method.
:param item_code: `item_code` of Item Document
@@ -245,7 +243,7 @@ def get_batch_no(item_code, warehouse, qty=1, throw=False):
"""
batch_no = None
batches = get_batches(item_code, warehouse, qty, throw)
batches = get_batches(item_code, warehouse, qty, throw, serial_no)
for batch in batches:
if cint(qty) <= cint(batch.qty):
@@ -260,16 +258,31 @@ def get_batch_no(item_code, warehouse, qty=1, throw=False):
return batch_no
def get_batches(item_code, warehouse, qty=1, throw=False):
batches = frappe.db.sql(
'select batch_id, sum(actual_qty) as qty from `tabBatch` join `tabStock Ledger Entry` ignore index (item_code, warehouse) '
'on (`tabBatch`.batch_id = `tabStock Ledger Entry`.batch_no )'
'where `tabStock Ledger Entry`.item_code = %s and `tabStock Ledger Entry`.warehouse = %s '
'and (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL)'
'group by batch_id '
'order by `tabBatch`.expiry_date ASC, `tabBatch`.creation ASC',
(item_code, warehouse),
as_dict=True
)
def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
cond = ''
if serial_no:
batch = frappe.get_all("Serial No",
fields = ["distinct batch_no"],
filters= {
"item_code": item_code,
"warehouse": warehouse,
"name": ("in", get_serial_nos(serial_no))
}
)
return batches
if batch and len(batch) > 1:
return []
cond = " and `tabBatch`.name = %s" %(frappe.db.escape(batch[0].batch_no))
return frappe.db.sql("""
select batch_id, sum(`tabStock Ledger Entry`.actual_qty) as qty
from `tabBatch`
join `tabStock Ledger Entry` ignore index (item_code, warehouse)
on (`tabBatch`.batch_id = `tabStock Ledger Entry`.batch_no )
where `tabStock Ledger Entry`.item_code = %s and `tabStock Ledger Entry`.warehouse = %s
and (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL) {0}
group by batch_id
order by `tabBatch`.expiry_date ASC, `tabBatch`.creation ASC
""".format(cond), (item_code, warehouse), as_dict=True)

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-24 19:29:09",
@@ -682,7 +683,7 @@
},
{
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"label": "Taxes and Charges Calculation",
"no_copy": 1,
"oldfieldtype": "HTML",
@@ -1238,7 +1239,8 @@
"icon": "fa fa-truck",
"idx": 146,
"is_submittable": 1,
"modified": "2019-09-27 14:24:20.269682",
"links": [],
"modified": "2019-12-30 19:17:13.122644",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",

View File

@@ -49,7 +49,7 @@ frappe.ui.form.on("Item", {
if (!frm.doc.is_fixed_asset) {
erpnext.item.make_dashboard(frm);
}
if (frm.doc.is_fixed_asset) {
frm.trigger('is_fixed_asset');
frm.trigger('auto_create_assets');
@@ -136,6 +136,20 @@ frappe.ui.form.on("Item", {
frm.toggle_reqd('customer', frm.doc.is_customer_provided_item ? 1:0);
},
gst_hsn_code: function(frm) {
if(!frm.doc.taxes || !frm.doc.taxes.length) {
frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc => {
$.each(hsn_doc.taxes || [], function(i, tax) {
let a = frappe.model.add_child(frm.doc, 'Item Tax', 'taxes');
a.item_tax_template = tax.item_tax_template;
a.tax_category = tax.tax_category;
a.valid_from = tax.valid_from;
frm.refresh_field('taxes');
});
});
}
},
is_fixed_asset: function(frm) {
// set serial no to false & toggles its visibility
frm.set_value('has_serial_no', 0);

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"allow_guest_to_view": 1,
"allow_import": 1,
"allow_rename": 1,
@@ -569,6 +570,7 @@
{
"default": "0.00",
"depends_on": "is_stock_item",
"description": "Minimum quantity should be as per Stock UOM",
"fieldname": "min_order_qty",
"fieldtype": "Float",
"label": "Minimum Order Qty",
@@ -1041,8 +1043,9 @@
"icon": "fa fa-tag",
"idx": 2,
"image_field": "image",
"links": [],
"max_attachments": 1,
"modified": "2019-12-13 12:15:56.197246",
"modified": "2020-01-02 19:13:59.295963",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@@ -551,7 +551,7 @@ class Item(WebsiteGenerator):
"""select parent from `tabItem Barcode` where barcode = %s and parent != %s""", (item_barcode.barcode, self.name))
if duplicate:
frappe.throw(_("Barcode {0} already used in Item {1}").format(
item_barcode.barcode, duplicate[0][0]), frappe.DuplicateEntryError)
item_barcode.barcode, duplicate[0][0]))
item_barcode.barcode_type = "" if item_barcode.barcode_type not in options else item_barcode.barcode_type
if item_barcode.barcode_type and item_barcode.barcode_type.upper() in ('EAN', 'UPC-A', 'EAN-13', 'EAN-8'):

File diff suppressed because it is too large Load Diff

View File

@@ -1,107 +1,50 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-02-22 01:28:01",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"editable_grid": 1,
"actions": [],
"creation": "2013-02-22 01:28:01",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"item_tax_template",
"tax_category",
"valid_from"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item_tax_template",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item Tax Template",
"length": 0,
"no_copy": 0,
"oldfieldname": "tax_type",
"oldfieldtype": "Link",
"options": "Item Tax Template",
"permlevel": 0,
"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
},
"fieldname": "item_tax_template",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item Tax Template",
"oldfieldname": "tax_type",
"oldfieldtype": "Link",
"options": "Item Tax Template",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "",
"fieldname": "tax_category",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Tax Category",
"length": 0,
"no_copy": 0,
"oldfieldname": "tax_rate",
"oldfieldtype": "Currency",
"options": "Tax Category",
"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
"fieldname": "tax_category",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Tax Category",
"oldfieldname": "tax_rate",
"oldfieldtype": "Currency",
"options": "Tax Category"
},
{
"fieldname": "valid_from",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Valid From"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-12-21 23:52:40.798944",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Tax",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"track_changes": 0,
"track_seen": 0,
"track_views": 0
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2019-12-28 21:54:40.807849",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Tax",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

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