Merge branch 'develop' into UX

This commit is contained in:
Marica 2021-04-12 10:51:24 +05:30 committed by GitHub
commit 99522252c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 342 additions and 273 deletions

View File

@ -328,6 +328,21 @@ class TestPricingRule(unittest.TestCase):
self.assertEquals(item.discount_amount, 110) self.assertEquals(item.discount_amount, 110)
self.assertEquals(item.rate, 990) self.assertEquals(item.rate, 990)
def test_pricing_rule_with_margin_and_discount_amount(self):
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10,
rate_or_discount="Discount Amount", discount_amount=110)
si = create_sales_invoice(do_not_save=True)
si.items[0].price_list_rate = 1000
si.payment_schedule = []
si.insert(ignore_permissions=True)
item = si.items[0]
self.assertEquals(item.margin_rate_or_amount, 10)
self.assertEquals(item.rate_with_margin, 1100)
self.assertEquals(item.discount_amount, 110)
self.assertEquals(item.rate, 990)
def test_pricing_rule_for_product_discount_on_same_item(self): def test_pricing_rule_for_product_discount_on_same_item(self):
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
test_record = { test_record = {
@ -560,6 +575,7 @@ def make_pricing_rule(**args):
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0, "margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
"condition": args.condition or '', "condition": args.condition or '',
"priority": 1, "priority": 1,
"discount_amount": args.discount_amount or 0.0,
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0 "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
}) })

View File

@ -127,7 +127,6 @@
"write_off_cost_center", "write_off_cost_center",
"advances_section", "advances_section",
"allocate_advances_automatically", "allocate_advances_automatically",
"adjust_advance_taxes",
"get_advances", "get_advances",
"advances", "advances",
"payment_schedule_section", "payment_schedule_section",
@ -1326,13 +1325,6 @@
"label": "Project", "label": "Project",
"options": "Project" "options": "Project"
}, },
{
"default": "0",
"description": "Taxes paid while advance payment will be adjusted against this invoice",
"fieldname": "adjust_advance_taxes",
"fieldtype": "Check",
"label": "Adjust Advance Taxes"
},
{ {
"depends_on": "eval:doc.is_internal_supplier", "depends_on": "eval:doc.is_internal_supplier",
"description": "Unrealized Profit / Loss account for intra-company transfers", "description": "Unrealized Profit / Loss account for intra-company transfers",
@ -1378,7 +1370,7 @@
"idx": 204, "idx": 204,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-03-09 21:12:30.422084", "modified": "2021-03-30 21:45:58.334107",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@ -406,9 +406,10 @@ def check_if_advance_entry_modified(args):
throw(_("""Payment Entry has been modified after you pulled it. Please pull it again.""")) throw(_("""Payment Entry has been modified after you pulled it. Please pull it again."""))
def validate_allocated_amount(args): def validate_allocated_amount(args):
precision = args.get('precision') or frappe.db.get_single_value("System Settings", "currency_precision")
if args.get("allocated_amount") < 0: if args.get("allocated_amount") < 0:
throw(_("Allocated amount cannot be negative")) throw(_("Allocated amount cannot be negative"))
elif args.get("allocated_amount") > args.get("unadjusted_amount"): elif flt(args.get("allocated_amount"), precision) > flt(args.get("unadjusted_amount"), precision):
throw(_("Allocated amount cannot be greater than unadjusted amount")) throw(_("Allocated amount cannot be greater than unadjusted amount"))
def update_reference_in_journal_entry(d, jv_obj): def update_reference_in_journal_entry(d, jv_obj):

View File

@ -659,6 +659,7 @@ class AccountsController(TransactionBase):
'dr_or_cr': dr_or_cr, 'dr_or_cr': dr_or_cr,
'unadjusted_amount': flt(d.advance_amount), 'unadjusted_amount': flt(d.advance_amount),
'allocated_amount': flt(d.allocated_amount), 'allocated_amount': flt(d.allocated_amount),
'precision': d.precision('advance_amount'),
'exchange_rate': (self.conversion_rate 'exchange_rate': (self.conversion_rate
if self.party_account_currency != self.company_currency else 1), if self.party_account_currency != self.company_currency else 1),
'grand_total': (self.base_grand_total 'grand_total': (self.base_grand_total

View File

@ -406,8 +406,7 @@ class StockController(AccountsController):
def set_rate_of_stock_uom(self): def set_rate_of_stock_uom(self):
if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]: if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]:
for d in self.get("items"): for d in self.get("items"):
if d.conversion_factor: d.stock_uom_rate = d.rate / (d.conversion_factor or 1)
d.stock_uom_rate = d.rate / d.conversion_factor
def validate_internal_transfer(self): def validate_internal_transfer(self):
if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \ if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \

View File

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

View File

@ -0,0 +1,62 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:source_name",
"creation": "2016-09-16 01:47:47.382372",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"source_name",
"details"
],
"fields": [
{
"fieldname": "source_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Source Name",
"reqd": 1,
"unique": 1
},
{
"fieldname": "details",
"fieldtype": "Text Editor",
"label": "Details"
}
],
"links": [],
"modified": "2021-02-08 12:51:48.971517",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead Source",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

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

View File

@ -1,12 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe # import frappe
import unittest import unittest
# test_records = frappe.get_test_records('Lead Source')
class TestLeadSource(unittest.TestCase): class TestLeadSource(unittest.TestCase):
pass pass

View File

@ -23,6 +23,7 @@
"rate_of_interest", "rate_of_interest",
"is_secured_loan", "is_secured_loan",
"disbursement_date", "disbursement_date",
"closure_date",
"disbursed_amount", "disbursed_amount",
"column_break_11", "column_break_11",
"maximum_loan_amount", "maximum_loan_amount",
@ -348,12 +349,18 @@
"no_copy": 1, "no_copy": 1,
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"read_only": 1 "read_only": 1
},
{
"fieldname": "closure_date",
"fieldtype": "Date",
"label": "Closure Date",
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-11-24 12:27:23.208240", "modified": "2021-04-10 09:28:21.946972",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Loan Management", "module": "Loan Management",
"name": "Loan", "name": "Loan",

View File

@ -523,33 +523,7 @@ class TestLoan(unittest.TestCase):
self.assertEqual(flt(repayment_entry.total_interest_paid, 0), flt(interest_amount, 0)) self.assertEqual(flt(repayment_entry.total_interest_paid, 0), flt(interest_amount, 0))
def test_penalty(self): def test_penalty(self):
pledge = [{ loan, amounts = create_loan_scenario_for_penalty(self)
"loan_security": "Test Security 1",
"qty": 4000.00
}]
loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
create_pledge(loan_application)
loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit()
self.assertEquals(loan.loan_amount, 1000000)
first_date = '2019-10-01'
last_date = '2019-10-30'
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
amounts = calculate_amounts(loan.name, add_days(last_date, 1))
paid_amount = amounts['interest_amount']/2
repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
paid_amount)
repayment_entry.submit()
# 30 days - grace period # 30 days - grace period
penalty_days = 30 - 4 penalty_days = 30 - 4
penalty_applicable_amount = flt(amounts['interest_amount']/2) penalty_applicable_amount = flt(amounts['interest_amount']/2)
@ -559,8 +533,28 @@ class TestLoan(unittest.TestCase):
calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual', calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual',
{'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount') {'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount')
self.assertEquals(loan.loan_amount, 1000000)
self.assertEquals(calculated_penalty_amount, penalty_amount) self.assertEquals(calculated_penalty_amount, penalty_amount)
def test_penalty_repayment(self):
loan, dummy = create_loan_scenario_for_penalty(self)
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:00')
first_penalty = 10000
second_penalty = amounts['penalty_amount'] - 10000
repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:00', 10000)
repayment_entry.submit()
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:01')
self.assertEquals(amounts['penalty_amount'], second_penalty)
repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:01', second_penalty)
repayment_entry.submit()
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:02')
self.assertEquals(amounts['penalty_amount'], 0)
def test_loan_write_off_limit(self): def test_loan_write_off_limit(self):
pledge = [{ pledge = [{
"loan_security": "Test Security 1", "loan_security": "Test Security 1",
@ -651,6 +645,32 @@ class TestLoan(unittest.TestCase):
amounts = calculate_amounts(loan.name, add_days(last_date, 5)) amounts = calculate_amounts(loan.name, add_days(last_date, 5))
self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0) self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0)
def create_loan_scenario_for_penalty(doc):
pledge = [{
"loan_security": "Test Security 1",
"qty": 4000.00
}]
loan_application = create_loan_application('_Test Company', doc.applicant2, 'Demand Loan', pledge)
create_pledge(loan_application)
loan = create_demand_loan(doc.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit()
first_date = '2019-10-01'
last_date = '2019-10-30'
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
amounts = calculate_amounts(loan.name, add_days(last_date, 1))
paid_amount = amounts['interest_amount']/2
repayment_entry = create_repayment_entry(loan.name, doc.applicant2, add_days(last_date, 5),
paid_amount)
repayment_entry.submit()
return loan, amounts
def create_loan_accounts(): def create_loan_accounts():
if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"): if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):

View File

@ -20,6 +20,10 @@
"cost_center", "cost_center",
"customer_details_section", "customer_details_section",
"bank_account", "bank_account",
"disbursement_references_section",
"reference_date",
"column_break_17",
"reference_number",
"amended_from" "amended_from"
], ],
"fields": [ "fields": [
@ -126,12 +130,31 @@
{ {
"fieldname": "column_break_8", "fieldname": "column_break_8",
"fieldtype": "Column Break" "fieldtype": "Column Break"
},
{
"fieldname": "disbursement_references_section",
"fieldtype": "Section Break",
"label": "Disbursement References"
},
{
"fieldname": "reference_date",
"fieldtype": "Date",
"label": "Reference Date"
},
{
"fieldname": "column_break_17",
"fieldtype": "Column Break"
},
{
"fieldname": "reference_number",
"fieldtype": "Data",
"label": "Reference Number"
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-11-06 10:04:30.882322", "modified": "2021-04-10 10:03:41.502210",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Loan Management", "module": "Loan Management",
"name": "Loan Disbursement", "name": "Loan Disbursement",

View File

@ -239,14 +239,16 @@
{ {
"fieldname": "total_penalty_paid", "fieldname": "total_penalty_paid",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 1,
"label": "Total Penalty Paid", "label": "Total Penalty Paid",
"options": "Company:company:default_currency" "options": "Company:company:default_currency",
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-04-05 13:45:19.137896", "modified": "2021-04-10 10:00:31.859076",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Loan Management", "module": "Loan Management",
"name": "Loan Repayment", "name": "Loan Repayment",

View File

@ -75,7 +75,7 @@ class LoanRepayment(AccountsController):
"docstatus": 1, "against_loan": self.against_loan}, 'posting_date') "docstatus": 1, "against_loan": self.against_loan}, 'posting_date')
if future_repayment_date: if future_repayment_date:
frappe.throw("Repayment already made till date {0}".format(getdate(future_repayment_date))) frappe.throw("Repayment already made till date {0}".format(get_datetime(future_repayment_date)))
def validate_amount(self): def validate_amount(self):
precision = cint(frappe.db.get_default("currency_precision")) or 2 precision = cint(frappe.db.get_default("currency_precision")) or 2
@ -83,10 +83,6 @@ class LoanRepayment(AccountsController):
if not self.amount_paid: if not self.amount_paid:
frappe.throw(_("Amount paid cannot be zero")) frappe.throw(_("Amount paid cannot be zero"))
if not self.shortfall_amount and self.amount_paid < self.penalty_amount:
msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount)
frappe.throw(msg)
def book_unaccrued_interest(self): def book_unaccrued_interest(self):
precision = cint(frappe.db.get_default("currency_precision")) or 2 precision = cint(frappe.db.get_default("currency_precision")) or 2
if self.total_interest_paid > self.interest_payable: if self.total_interest_paid > self.interest_payable:
@ -231,6 +227,14 @@ class LoanRepayment(AccountsController):
gle_map = [] gle_map = []
loan_details = frappe.get_doc("Loan", self.against_loan) loan_details = frappe.get_doc("Loan", self.against_loan)
if self.shortfall_amount and self.amount_paid > self.shortfall_amount:
remarks = _("Shortfall Repayment of {0}.\nRepayment against Loan: {1}").format(self.shortfall_amount,
self.against_loan)
elif self.shortfall_amount:
remarks = _("Shortfall Repayment of {0}").format(self.shortfall_amount)
else:
remarks = _("Repayment against Loan: ") + self.against_loan
if self.total_penalty_paid: if self.total_penalty_paid:
gle_map.append( gle_map.append(
self.get_gl_dict({ self.get_gl_dict({
@ -271,7 +275,7 @@ class LoanRepayment(AccountsController):
"debit_in_account_currency": self.amount_paid, "debit_in_account_currency": self.amount_paid,
"against_voucher_type": "Loan", "against_voucher_type": "Loan",
"against_voucher": self.against_loan, "against_voucher": self.against_loan,
"remarks": _("Repayment against Loan: ") + self.against_loan, "remarks": remarks,
"cost_center": self.cost_center, "cost_center": self.cost_center,
"posting_date": getdate(self.posting_date) "posting_date": getdate(self.posting_date)
}) })
@ -287,7 +291,7 @@ class LoanRepayment(AccountsController):
"credit_in_account_currency": self.amount_paid, "credit_in_account_currency": self.amount_paid,
"against_voucher_type": "Loan", "against_voucher_type": "Loan",
"against_voucher": self.against_loan, "against_voucher": self.against_loan,
"remarks": _("Repayment against Loan: ") + self.against_loan, "remarks": remarks,
"cost_center": self.cost_center, "cost_center": self.cost_center,
"posting_date": getdate(self.posting_date) "posting_date": getdate(self.posting_date)
}) })
@ -338,6 +342,18 @@ def get_accrued_interest_entries(against_loan, posting_date=None):
return unpaid_accrued_entries return unpaid_accrued_entries
def get_penalty_details(against_loan):
penalty_details = frappe.db.sql("""
SELECT posting_date, (penalty_amount - total_penalty_paid) as pending_penalty_amount
FROM `tabLoan Repayment` where posting_date >= (SELECT MAX(posting_date) from `tabLoan Repayment`
where against_loan = %s) and docstatus = 1 and against_loan = %s
""", (against_loan, against_loan))
if penalty_details:
return penalty_details[0][0], flt(penalty_details[0][1])
else:
return None, 0
# This function returns the amounts that are payable at the time of loan repayment based on posting date # This function returns the amounts that are payable at the time of loan repayment based on posting date
# So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
@ -348,6 +364,7 @@ def get_amounts(amounts, against_loan, posting_date):
loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type) loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type)
accrued_interest_entries = get_accrued_interest_entries(against_loan_doc.name, posting_date) accrued_interest_entries = get_accrued_interest_entries(against_loan_doc.name, posting_date)
computed_penalty_date, pending_penalty_amount = get_penalty_details(against_loan)
pending_accrual_entries = {} pending_accrual_entries = {}
total_pending_interest = 0 total_pending_interest = 0
@ -362,8 +379,13 @@ def get_amounts(amounts, against_loan, posting_date):
# and if no_of_late days are positive then penalty is levied # and if no_of_late days are positive then penalty is levied
due_date = add_days(entry.posting_date, 1) due_date = add_days(entry.posting_date, 1)
no_of_late_days = date_diff(posting_date, due_date_after_grace_period = add_days(due_date, loan_type_details.grace_period_in_days)
add_days(due_date, loan_type_details.grace_period_in_days)) + 1
# Consider one day after already calculated penalty
if computed_penalty_date and getdate(computed_penalty_date) >= due_date_after_grace_period:
due_date_after_grace_period = add_days(computed_penalty_date, 1)
no_of_late_days = date_diff(posting_date, due_date_after_grace_period) + 1
if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary) and entry.accrual_type == 'Regular': if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary) and entry.accrual_type == 'Regular':
penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days) penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)
@ -401,7 +423,7 @@ def get_amounts(amounts, against_loan, posting_date):
amounts["pending_principal_amount"] = flt(pending_principal_amount, precision) amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
amounts["payable_principal_amount"] = flt(payable_principal_amount, precision) amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
amounts["interest_amount"] = flt(total_pending_interest, precision) amounts["interest_amount"] = flt(total_pending_interest, precision)
amounts["penalty_amount"] = flt(penalty_amount, precision) amounts["penalty_amount"] = flt(penalty_amount + pending_penalty_amount, precision)
amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision) amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
amounts["pending_accrual_entries"] = pending_accrual_entries amounts["pending_accrual_entries"] = pending_accrual_entries
amounts["unaccrued_interest"] = flt(unaccrued_interest, precision) amounts["unaccrued_interest"] = flt(unaccrued_interest, precision)

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import get_datetime, flt from frappe.utils import get_datetime, flt, getdate
import json import json
from six import iteritems from six import iteritems
from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price
@ -113,7 +113,11 @@ class LoanSecurityUnpledge(Document):
pledged_qty += qty pledged_qty += qty
if not pledged_qty: if not pledged_qty:
frappe.db.set_value('Loan', self.loan, 'status', 'Closed') frappe.db.set_value('Loan', self.loan,
{
'status': 'Closed',
'closure_date': getdate()
})
@frappe.whitelist() @frappe.whitelist()
def get_pledged_security_qty(loan): def get_pledged_security_qty(loan):

View File

@ -763,4 +763,5 @@ erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
erpnext.patches.v13_0.setup_uae_vat_fields erpnext.patches.v13_0.setup_uae_vat_fields
execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext') execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
erpnext.patches.v13_0.rename_discharge_date_in_ip_record erpnext.patches.v13_0.rename_discharge_date_in_ip_record
erpnext.patches.v12_0.add_gst_category_in_delivery_note
erpnext.patches.v12_0.purchase_receipt_status erpnext.patches.v12_0.purchase_receipt_status

View File

@ -0,0 +1,19 @@
from __future__ import unicode_literals
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
custom_fields = {
'Delivery Note': [
dict(fieldname='gst_category', label='GST Category',
fieldtype='Select', insert_after='gst_vehicle_type', print_hide=1,
options='\nRegistered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders',
fetch_from='customer.gst_category', fetch_if_empty=1),
]
}
create_custom_fields(custom_fields, update=True)

View File

@ -18,6 +18,7 @@ def execute():
for old_dt, new_dt in doctypes.items(): for old_dt, new_dt in doctypes.items():
if not frappe.db.table_exists(new_dt) and frappe.db.table_exists(old_dt): if not frappe.db.table_exists(new_dt) and frappe.db.table_exists(old_dt):
frappe.reload_doc('healthcare', 'doctype', frappe.scrub(old_dt))
frappe.rename_doc('DocType', old_dt, new_dt, force=True) frappe.rename_doc('DocType', old_dt, new_dt, force=True)
frappe.reload_doc('healthcare', 'doctype', frappe.scrub(new_dt)) frappe.reload_doc('healthcare', 'doctype', frappe.scrub(new_dt))
frappe.delete_doc_if_exists('DocType', old_dt) frappe.delete_doc_if_exists('DocType', old_dt)
@ -36,6 +37,18 @@ def execute():
SET parentfield = %(parentfield)s SET parentfield = %(parentfield)s
""".format(doctype), {'parentfield': parentfield}) """.format(doctype), {'parentfield': parentfield})
# copy renamed child table fields (fields were already renamed in old doctype json, hence sql)
frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_name = test_name""")
frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_event = test_event""")
frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_uom = test_uom""")
frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_comment = test_comment""")
frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_event = test_event""")
frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_uom = test_uom""")
frappe.db.sql("""UPDATE `tabDescriptive Test Result` SET lab_test_particulars = test_particulars""")
frappe.db.sql("""UPDATE `tabLab Test Group Template` SET lab_test_template = test_template""")
frappe.db.sql("""UPDATE `tabLab Test Group Template` SET lab_test_description = test_description""")
frappe.db.sql("""UPDATE `tabLab Test Group Template` SET lab_test_rate = test_rate""")
# rename field # rename field
frappe.reload_doc('healthcare', 'doctype', 'lab_test') frappe.reload_doc('healthcare', 'doctype', 'lab_test')
if frappe.db.has_column('Lab Test', 'special_toggle'): if frappe.db.has_column('Lab Test', 'special_toggle'):

View File

@ -20,9 +20,11 @@ def execute():
frappe.clear_cache() frappe.clear_cache()
frappe.flags.warehouse_account_map = {} frappe.flags.warehouse_account_map = {}
company_list = []
data = frappe.db.sql(''' data = frappe.db.sql('''
SELECT SELECT
name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time, company
FROM FROM
`tabStock Ledger Entry` `tabStock Ledger Entry`
WHERE WHERE
@ -36,6 +38,9 @@ def execute():
total_sle = len(data) total_sle = len(data)
i = 0 i = 0
for d in data: for d in data:
if d.company not in company_list:
company_list.append(d.company)
update_entries_after({ update_entries_after({
"item_code": d.item_code, "item_code": d.item_code,
"warehouse": d.warehouse, "warehouse": d.warehouse,
@ -53,8 +58,10 @@ def execute():
print("Reposting General Ledger Entries...") print("Reposting General Ledger Entries...")
for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}): if data:
update_gl_entries_after(posting_date, posting_time, company=row.name) for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
if row.name in company_list:
update_gl_entries_after(posting_date, posting_time, company=row.name)
frappe.db.auto_commit_on_many_writes = 0 frappe.db.auto_commit_on_many_writes = 0

View File

@ -8,6 +8,7 @@ def execute():
frappe.reload_doc("healthcare", "doctype", "Therapy Session") frappe.reload_doc("healthcare", "doctype", "Therapy Session")
frappe.reload_doc("healthcare", "doctype", "Inpatient Medication Order") frappe.reload_doc("healthcare", "doctype", "Inpatient Medication Order")
frappe.reload_doc("healthcare", "doctype", "Clinical Procedure")
frappe.reload_doc("healthcare", "doctype", "Patient History Settings") frappe.reload_doc("healthcare", "doctype", "Patient History Settings")
frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type") frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type")
frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type") frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type")

View File

@ -5,7 +5,7 @@ from frappe import _
def execute(): def execute():
from erpnext.setup.setup_wizard.operations.install_fixtures import default_lead_sources from erpnext.setup.setup_wizard.operations.install_fixtures import default_lead_sources
frappe.reload_doc('selling', 'doctype', 'lead_source') frappe.reload_doc('crm', 'doctype', 'lead_source')
frappe.local.lang = frappe.db.get_default("lang") or 'en' frappe.local.lang = frappe.db.get_default("lang") or 'en'

View File

@ -33,12 +33,16 @@ class TestProject(unittest.TestCase):
def test_project_template_having_parent_child_tasks(self): def test_project_template_having_parent_child_tasks(self):
project_name = "Test Project with Template - Tasks with Parent-Child Relation" project_name = "Test Project with Template - Tasks with Parent-Child Relation"
if frappe.db.get_value('Project', {'project_name': project_name}, 'name'):
project_name = frappe.db.get_value('Project', {'project_name': project_name}, 'name')
frappe.db.sql(""" delete from tabTask where project = %s """, project_name) frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
frappe.delete_doc('Project', project_name) frappe.delete_doc('Project', project_name)
task1 = task_exists("Test Template Task Parent") task1 = task_exists("Test Template Task Parent")
if not task1: if not task1:
task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=4) task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=10)
task2 = task_exists("Test Template Task Child 1") task2 = task_exists("Test Template Task Child 1")
if not task2: if not task2:
@ -53,7 +57,7 @@ class TestProject(unittest.TestCase):
tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc') tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc')
self.assertEqual(tasks[0].subject, 'Test Template Task Parent') self.assertEqual(tasks[0].subject, 'Test Template Task Parent')
self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 4)) self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 10))
self.assertEqual(tasks[1].subject, 'Test Template Task Child 1') self.assertEqual(tasks[1].subject, 'Test Template Task Child 1')
self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3)) self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))

View File

@ -156,6 +156,13 @@ def make_custom_fields(update=True):
fetch_if_empty=1), fetch_if_empty=1),
] ]
delivery_note_gst_category = [
dict(fieldname='gst_category', label='GST Category',
fieldtype='Select', insert_after='gst_vehicle_type', print_hide=1,
options='\nRegistered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders',
fetch_from='customer.gst_category', fetch_if_empty=1),
]
invoice_gst_fields = [ invoice_gst_fields = [
dict(fieldname='invoice_copy', label='Invoice Copy', dict(fieldname='invoice_copy', label='Invoice Copy',
fieldtype='Select', insert_after='export_type', print_hide=1, allow_on_submit=1, fieldtype='Select', insert_after='export_type', print_hide=1, allow_on_submit=1,
@ -280,7 +287,7 @@ def make_custom_fields(update=True):
'allow_on_submit': 1, 'allow_on_submit': 1,
'insert_after': 'customer_name_in_arabic', 'insert_after': 'customer_name_in_arabic',
'translatable': 0, 'translatable': 0,
} }
] ]
si_ewaybill_fields = [ si_ewaybill_fields = [
@ -438,7 +445,7 @@ def make_custom_fields(update=True):
'Purchase Order': purchase_invoice_gst_fields, 'Purchase Order': purchase_invoice_gst_fields,
'Purchase Receipt': purchase_invoice_gst_fields, 'Purchase Receipt': purchase_invoice_gst_fields,
'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields + si_einvoice_fields, 'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields + si_einvoice_fields,
'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields, 'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields + delivery_note_gst_category,
'Sales Order': sales_invoice_gst_fields, 'Sales Order': sales_invoice_gst_fields,
'Tax Category': inter_state_gst_field, 'Tax Category': inter_state_gst_field,
'Item': [ 'Item': [

View File

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

View File

@ -1,131 +0,0 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "field:source_name",
"beta": 0,
"creation": "2016-09-16 01:47:47.382372",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "source_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Source Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "details",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 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-09-16 02:03:01.441622",
"modified_by": "Administrator",
"module": "Selling",
"name": "Lead Source",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 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": "Sales Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 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": "Sales User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

@ -279,11 +279,6 @@ erpnext.PointOfSale.Controller = class {
const item_row = frappe.model.get_doc(cdt, cdn); const item_row = frappe.model.get_doc(cdt, cdn);
if (item_row && item_row[fieldname] != value) { if (item_row && item_row[fieldname] != value) {
if (fieldname === 'qty' && flt(value) == 0) {
this.remove_item_from_cart();
return;
}
const { item_code, batch_no, uom } = this.item_details.current_item; const { item_code, batch_no, uom } = this.item_details.current_item;
const event = { const event = {
field: fieldname, field: fieldname,

View File

@ -27,7 +27,7 @@ def delete_company_transactions(company_name):
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget", if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
"Party Account", "Employee", "Sales Taxes and Charges Template", "Party Account", "Employee", "Sales Taxes and Charges Template",
"Purchase Taxes and Charges Template", "POS Profile", "BOM", "Purchase Taxes and Charges Template", "POS Profile", "BOM",
"Company", "Bank Account", "Item Tax Template", "Mode Of Payment", "Company", "Bank Account", "Item Tax Template", "Mode Of Payment", "Mode of Payment Account",
"Item Default", "Customer", "Supplier", "GST Account"): "Item Default", "Customer", "Supplier", "GST Account"):
delete_for_doctype(doctype, company_name) delete_for_doctype(doctype, company_name)

View File

@ -494,7 +494,8 @@ def make_item_variant():
test_records = frappe.get_test_records('Item') test_records = frappe.get_test_records('Item')
def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None, is_customer_provided_item=None, customer=None, is_purchase_item=None, opening_stock=None): def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None, is_customer_provided_item=None,
customer=None, is_purchase_item=None, opening_stock=None, company=None):
if not frappe.db.exists("Item", item_code): if not frappe.db.exists("Item", item_code):
item = frappe.new_doc("Item") item = frappe.new_doc("Item")
item.item_code = item_code item.item_code = item_code
@ -509,7 +510,7 @@ def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None,
item.customer = customer or '' item.customer = customer or ''
item.append("item_defaults", { item.append("item_defaults", {
"default_warehouse": warehouse or '_Test Warehouse - _TC', "default_warehouse": warehouse or '_Test Warehouse - _TC',
"company": "_Test Company" "company": company or "_Test Company"
}) })
item.save() item.save()
else: else:

View File

@ -379,7 +379,6 @@ def create_stock_entry(pick_list):
else: else:
stock_entry = update_stock_entry_items_with_no_reference(pick_list, stock_entry) stock_entry = update_stock_entry_items_with_no_reference(pick_list, stock_entry)
stock_entry.set_incoming_rate()
stock_entry.set_actual_qty() stock_entry.set_actual_qty()
stock_entry.calculate_rate_and_amount() stock_entry.calculate_rate_and_amount()

View File

@ -124,7 +124,7 @@ def repost_entries():
return return
for d in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}): for d in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
check_if_stock_and_account_balance_synced(today(), d.company) check_if_stock_and_account_balance_synced(today(), d.name)
def get_repost_item_valuation_entries(): def get_repost_item_valuation_entries():
date = add_to_date(today(), hours=-12) date = add_to_date(today(), hours=-12)

View File

@ -179,11 +179,15 @@ class TestStockEntry(unittest.TestCase):
def test_material_transfer_gl_entry(self): def test_material_transfer_gl_entry(self):
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
mtn = make_stock_entry(item_code="_Test Item", source="Stores - TCP1", item_code = 'Hand Sanitizer - 001'
create_item(item_code =item_code, is_stock_item = 1,
is_purchase_item=1, opening_stock=1000, valuation_rate=10, company=company, warehouse="Stores - TCP1")
mtn = make_stock_entry(item_code=item_code, source="Stores - TCP1",
target="Finished Goods - TCP1", qty=45, company=company) target="Finished Goods - TCP1", qty=45, company=company)
self.check_stock_ledger_entries("Stock Entry", mtn.name, self.check_stock_ledger_entries("Stock Entry", mtn.name,
[["_Test Item", "Stores - TCP1", -45.0], ["_Test Item", "Finished Goods - TCP1", 45.0]]) [[item_code, "Stores - TCP1", -45.0], [item_code, "Finished Goods - TCP1", 45.0]])
source_warehouse_account = get_inventory_account(mtn.company, mtn.get("items")[0].s_warehouse) source_warehouse_account = get_inventory_account(mtn.company, mtn.get("items")[0].s_warehouse)

View File

@ -48,44 +48,62 @@ frappe.ui.form.on("Issue", {
} }
}, },
refresh: function (frm) { refresh: function(frm) {
if (frm.doc.status !== "Closed") {
if (frm.doc.service_level_agreement && frm.doc.agreement_status === "Ongoing") {
frappe.call({
"method": "frappe.client.get",
args: {
doctype: "Service Level Agreement",
name: frm.doc.service_level_agreement
},
callback: function(data) {
let statuses = data.message.pause_sla_on;
const hold_statuses = [];
$.each(statuses, (_i, entry) => {
hold_statuses.push(entry.status);
});
if (hold_statuses.includes(frm.doc.status)) {
frm.dashboard.clear_headline();
let message = {"indicator": "orange", "msg": __("SLA is on hold since {0}", [moment(frm.doc.on_hold_since).fromNow(true)])};
frm.dashboard.set_headline_alert(
'<div class="row">' +
'<div class="col-xs-12">' +
'<span class="indicator whitespace-nowrap '+ message.indicator +'"><span>'+ message.msg +'</span></span> ' +
'</div>' +
'</div>'
);
} else {
set_time_to_resolve_and_response(frm);
}
}
});
}
frm.add_custom_button(__("Close"), function () { // alert messages
if (frm.doc.status !== "Closed" && frm.doc.service_level_agreement
&& frm.doc.agreement_status === "Ongoing") {
frappe.call({
"method": "frappe.client.get",
args: {
doctype: "Service Level Agreement",
name: frm.doc.service_level_agreement
},
callback: function(data) {
let statuses = data.message.pause_sla_on;
const hold_statuses = [];
$.each(statuses, (_i, entry) => {
hold_statuses.push(entry.status);
});
if (hold_statuses.includes(frm.doc.status)) {
frm.dashboard.clear_headline();
let message = { "indicator": "orange", "msg": __("SLA is on hold since {0}", [moment(frm.doc.on_hold_since).fromNow(true)]) };
frm.dashboard.set_headline_alert(
'<div class="row">' +
'<div class="col-xs-12">' +
'<span class="indicator whitespace-nowrap ' + message.indicator + '"><span>' + message.msg + '</span></span> ' +
'</div>' +
'</div>'
);
} else {
set_time_to_resolve_and_response(frm);
}
}
});
} else if (frm.doc.service_level_agreement) {
frm.dashboard.clear_headline();
let agreement_status = (frm.doc.agreement_status == "Fulfilled") ?
{ "indicator": "green", "msg": "Service Level Agreement has been fulfilled" } :
{ "indicator": "red", "msg": "Service Level Agreement Failed" };
frm.dashboard.set_headline_alert(
'<div class="row">' +
'<div class="col-xs-12">' +
'<span class="indicator whitespace-nowrap ' + agreement_status.indicator + '"><span class="hidden-xs">' + agreement_status.msg + '</span></span> ' +
'</div>' +
'</div>'
);
}
// buttons
if (frm.doc.status !== "Closed") {
frm.add_custom_button(__("Close"), function() {
frm.set_value("status", "Closed"); frm.set_value("status", "Closed");
frm.save(); frm.save();
}); });
frm.add_custom_button(__("Task"), function () { frm.add_custom_button(__("Task"), function() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.support.doctype.issue.issue.make_task", method: "erpnext.support.doctype.issue.issue.make_task",
frm: frm frm: frm
@ -93,23 +111,7 @@ frappe.ui.form.on("Issue", {
}, __("Create")); }, __("Create"));
} else { } else {
if (frm.doc.service_level_agreement) { frm.add_custom_button(__("Reopen"), function() {
frm.dashboard.clear_headline();
let agreement_status = (frm.doc.agreement_status == "Fulfilled") ?
{"indicator": "green", "msg": "Service Level Agreement has been fulfilled"} :
{"indicator": "red", "msg": "Service Level Agreement Failed"};
frm.dashboard.set_headline_alert(
'<div class="row">' +
'<div class="col-xs-12">' +
'<span class="indicator whitespace-nowrap '+ agreement_status.indicator +'"><span class="hidden-xs">'+ agreement_status.msg +'</span></span> ' +
'</div>' +
'</div>'
);
}
frm.add_custom_button(__("Reopen"), function () {
frm.set_value("status", "Open"); frm.set_value("status", "Open");
frm.save(); frm.save();
}); });

View File

@ -7,7 +7,7 @@ import json
from frappe import _ from frappe import _
from frappe import utils from frappe import utils
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import now_datetime, getdate, get_weekdays, add_to_date, get_time, get_datetime, time_diff_in_seconds from frappe.utils import cint, now_datetime, getdate, get_weekdays, add_to_date, get_time, get_datetime, time_diff_in_seconds
from datetime import datetime, timedelta from datetime import datetime, timedelta
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from frappe.utils.user import is_website_user from frappe.utils.user import is_website_user
@ -128,8 +128,8 @@ class Issue(Document):
def update_agreement_status(self): def update_agreement_status(self):
if self.service_level_agreement and self.agreement_status == "Ongoing": if self.service_level_agreement and self.agreement_status == "Ongoing":
if frappe.db.get_value("Issue", self.name, "response_by_variance") < 0 or \ if cint(frappe.db.get_value("Issue", self.name, "response_by_variance")) < 0 or \
frappe.db.get_value("Issue", self.name, "resolution_by_variance") < 0: cint(frappe.db.get_value("Issue", self.name, "resolution_by_variance")) < 0:
self.agreement_status = "Failed" self.agreement_status = "Failed"
else: else: