Merge branch 'develop' of https://github.com/frappe/erpnext into planning
This commit is contained in:
commit
661b1e59c1
32
.flake8
Normal file
32
.flake8
Normal file
@ -0,0 +1,32 @@
|
||||
[flake8]
|
||||
ignore =
|
||||
E121,
|
||||
E126,
|
||||
E127,
|
||||
E128,
|
||||
E203,
|
||||
E225,
|
||||
E226,
|
||||
E231,
|
||||
E241,
|
||||
E251,
|
||||
E261,
|
||||
E265,
|
||||
E302,
|
||||
E303,
|
||||
E305,
|
||||
E402,
|
||||
E501,
|
||||
E741,
|
||||
W291,
|
||||
W292,
|
||||
W293,
|
||||
W391,
|
||||
W503,
|
||||
W504,
|
||||
F403,
|
||||
B007,
|
||||
B950,
|
||||
W191,
|
||||
|
||||
max-line-length = 200
|
2
.github/helper/install.sh
vendored
2
.github/helper/install.sh
vendored
@ -12,7 +12,7 @@ sudo apt install npm
|
||||
|
||||
pip install frappe-bench
|
||||
|
||||
git clone https://github.com/frappe/frappe --branch "${GITHUB_BASE_REF}" --depth 1
|
||||
git clone https://github.com/frappe/frappe --branch "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}" --depth 1
|
||||
bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench
|
||||
|
||||
mkdir ~/frappe-bench/sites/test_site
|
||||
|
6
.github/workflows/ci-tests.yml
vendored
6
.github/workflows/ci-tests.yml
vendored
@ -1,12 +1,10 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
on: [pull_request, workflow_dispatch, push]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-18.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -5,7 +5,7 @@
|
||||
<p>ERP made simple</p>
|
||||
</p>
|
||||
|
||||
[![Build Status](https://api.travis-ci.com/frappe/erpnext.svg?branch=develop)](https://travis-ci.com/frappe/erpnext)
|
||||
[![CI](https://github.com/frappe/erpnext/actions/workflows/ci-tests.yml/badge.svg?branch=develop)](https://github.com/frappe/erpnext/actions/workflows/ci-tests.yml)
|
||||
[![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop)
|
||||
|
||||
|
@ -11,36 +11,36 @@ from erpnext.accounts.doctype.accounting_period.accounting_period import Overlap
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
class TestAccountingPeriod(unittest.TestCase):
|
||||
def test_overlap(self):
|
||||
ap1 = create_accounting_period(start_date = "2018-04-01",
|
||||
end_date = "2018-06-30", company = "Wind Power LLC")
|
||||
ap1.save()
|
||||
def test_overlap(self):
|
||||
ap1 = create_accounting_period(start_date = "2018-04-01",
|
||||
end_date = "2018-06-30", company = "Wind Power LLC")
|
||||
ap1.save()
|
||||
|
||||
ap2 = create_accounting_period(start_date = "2018-06-30",
|
||||
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
|
||||
self.assertRaises(OverlapError, ap2.save)
|
||||
ap2 = create_accounting_period(start_date = "2018-06-30",
|
||||
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
|
||||
self.assertRaises(OverlapError, ap2.save)
|
||||
|
||||
def test_accounting_period(self):
|
||||
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
|
||||
ap1.save()
|
||||
def test_accounting_period(self):
|
||||
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
|
||||
ap1.save()
|
||||
|
||||
doc = create_sales_invoice(do_not_submit=1, cost_center = "_Test Company - _TC", warehouse = "Stores - _TC")
|
||||
self.assertRaises(ClosedAccountingPeriod, doc.submit)
|
||||
doc = create_sales_invoice(do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
|
||||
self.assertRaises(ClosedAccountingPeriod, doc.submit)
|
||||
|
||||
def tearDown(self):
|
||||
for d in frappe.get_all("Accounting Period"):
|
||||
frappe.delete_doc("Accounting Period", d.name)
|
||||
def tearDown(self):
|
||||
for d in frappe.get_all("Accounting Period"):
|
||||
frappe.delete_doc("Accounting Period", d.name)
|
||||
|
||||
def create_accounting_period(**args):
|
||||
args = frappe._dict(args)
|
||||
args = frappe._dict(args)
|
||||
|
||||
accounting_period = frappe.new_doc("Accounting Period")
|
||||
accounting_period.start_date = args.start_date or nowdate()
|
||||
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
|
||||
accounting_period.company = args.company or "_Test Company"
|
||||
accounting_period.period_name =args.period_name or "_Test_Period_Name_1"
|
||||
accounting_period.append("closed_documents", {
|
||||
"document_type": 'Sales Invoice', "closed": 1
|
||||
})
|
||||
accounting_period = frappe.new_doc("Accounting Period")
|
||||
accounting_period.start_date = args.start_date or nowdate()
|
||||
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
|
||||
accounting_period.company = args.company or "_Test Company"
|
||||
accounting_period.period_name =args.period_name or "_Test_Period_Name_1"
|
||||
accounting_period.append("closed_documents", {
|
||||
"document_type": 'Sales Invoice', "closed": 1
|
||||
})
|
||||
|
||||
return accounting_period
|
||||
return accounting_period
|
||||
|
@ -6,10 +6,12 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
test_dependencies = ["Customer", "Supplier"]
|
||||
from frappe.cache_manager import clear_doctype_cache
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
|
||||
|
||||
test_dependencies = ["Customer", "Supplier"]
|
||||
|
||||
class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||
def setUp(self):
|
||||
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
||||
@ -24,22 +26,25 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||
|
||||
def test_opening_sales_invoice_creation(self):
|
||||
property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check")
|
||||
invoices = self.make_invoices(company="_Test Opening Invoice Company")
|
||||
try:
|
||||
invoices = self.make_invoices(company="_Test Opening Invoice Company")
|
||||
|
||||
self.assertEqual(len(invoices), 2)
|
||||
expected_value = {
|
||||
"keys": ["customer", "outstanding_amount", "status"],
|
||||
0: ["_Test Customer", 300, "Overdue"],
|
||||
1: ["_Test Customer 1", 250, "Overdue"],
|
||||
}
|
||||
self.check_expected_values(invoices, expected_value)
|
||||
self.assertEqual(len(invoices), 2)
|
||||
expected_value = {
|
||||
"keys": ["customer", "outstanding_amount", "status"],
|
||||
0: ["_Test Customer", 300, "Overdue"],
|
||||
1: ["_Test Customer 1", 250, "Overdue"],
|
||||
}
|
||||
self.check_expected_values(invoices, expected_value)
|
||||
|
||||
si = frappe.get_doc("Sales Invoice", invoices[0])
|
||||
si = frappe.get_doc("Sales Invoice", invoices[0])
|
||||
|
||||
# Check if update stock is not enabled
|
||||
self.assertEqual(si.update_stock, 0)
|
||||
# Check if update stock is not enabled
|
||||
self.assertEqual(si.update_stock, 0)
|
||||
|
||||
property_setter.delete()
|
||||
finally:
|
||||
property_setter.delete()
|
||||
clear_doctype_cache("Sales Invoice")
|
||||
|
||||
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
|
||||
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
|
||||
|
@ -605,12 +605,22 @@ frappe.ui.form.on('Payment Entry', {
|
||||
{fieldtype:"Column Break"},
|
||||
{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
|
||||
{fieldtype:"Section Break"},
|
||||
{fieldtype:"Link", label:__("Cost Center"), fieldname:"cost_center", options:"Cost Center",
|
||||
"get_query": function() {
|
||||
return {
|
||||
"filters": {"company": frm.doc.company}
|
||||
}
|
||||
}
|
||||
},
|
||||
{fieldtype:"Column Break"},
|
||||
{fieldtype:"Section Break"},
|
||||
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
|
||||
];
|
||||
|
||||
frappe.prompt(fields, function(filters){
|
||||
frappe.flags.allocate_payment_amount = true;
|
||||
frm.events.validate_filters_data(frm, filters);
|
||||
frm.doc.cost_center = filters.cost_center;
|
||||
frm.events.get_outstanding_documents(frm, filters);
|
||||
}, __("Filters"), __("Get Outstanding Documents"));
|
||||
},
|
||||
@ -1066,11 +1076,6 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.set_value("paid_from_account_balance", r.message.paid_from_account_balance);
|
||||
frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance);
|
||||
frm.set_value("party_balance", r.message.party_balance);
|
||||
},
|
||||
() => {
|
||||
if(frm.doc.payment_type != "Internal") {
|
||||
frm.clear_table("references");
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
|
@ -9,8 +9,16 @@ from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profi
|
||||
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
class TestPOSInvoice(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
if frappe.session.user != "Administrator":
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
if frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
||||
frappe.db.set_value("Selling Settings", None, "validate_selling_price", 0)
|
||||
|
||||
def test_timestamp_change(self):
|
||||
w = create_pos_invoice(do_not_save=1)
|
||||
w.docstatus = 0
|
||||
@ -370,7 +378,6 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
pos_inv.load_from_db()
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||
self.assertEqual(rounded_total, 3470)
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||
@ -412,7 +419,6 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
pos_inv.load_from_db()
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||
self.assertEqual(rounded_total, 840)
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
def test_merging_with_validate_selling_price(self):
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||
@ -421,10 +427,12 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
||||
frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
|
||||
|
||||
make_purchase_receipt(item_code="_Test Item", warehouse="_Test Warehouse - _TC", qty=1, rate=300)
|
||||
item = "Test Selling Price Validation"
|
||||
make_item(item, {"is_stock_item": 1})
|
||||
make_purchase_receipt(item_code=item, warehouse="_Test Warehouse - _TC", qty=1, rate=300)
|
||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
||||
pos_inv = create_pos_invoice(item=item, rate=300, do_not_submit=1)
|
||||
pos_inv.append('payments', {
|
||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
||||
})
|
||||
@ -438,7 +446,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
})
|
||||
self.assertRaises(frappe.ValidationError, pos_inv.submit)
|
||||
|
||||
pos_inv2 = create_pos_invoice(rate=400, do_not_submit=1)
|
||||
pos_inv2 = create_pos_invoice(item=item, rate=400, do_not_submit=1)
|
||||
pos_inv2.append('payments', {
|
||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400
|
||||
})
|
||||
@ -457,8 +465,6 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
pos_inv2.load_from_db()
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
|
||||
self.assertEqual(rounded_total, 400)
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 0)
|
||||
|
||||
def create_pos_invoice(**args):
|
||||
args = frappe._dict(args)
|
||||
|
@ -24,10 +24,11 @@ class POSOpeningEntry(StatusUpdater):
|
||||
def validate_payment_method_account(self):
|
||||
invalid_modes = []
|
||||
for d in self.balance_details:
|
||||
account = frappe.db.get_value("Mode of Payment Account",
|
||||
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
|
||||
if not account:
|
||||
invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
|
||||
if d.mode_of_payment:
|
||||
account = frappe.db.get_value("Mode of Payment Account",
|
||||
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
|
||||
if not account:
|
||||
invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
|
||||
|
||||
if invalid_modes:
|
||||
if invalid_modes == 1:
|
||||
|
@ -1800,6 +1800,15 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.selling_price_list = "_Test Price List Rest of the World"
|
||||
si.update_stock = 1
|
||||
si.items[0].target_warehouse = 'Work In Progress - TCP1'
|
||||
|
||||
# Add stock to stores for succesful stock transfer
|
||||
make_stock_entry(
|
||||
target="Stores - TCP1",
|
||||
company = "_Test Company with perpetual inventory",
|
||||
qty=1,
|
||||
basic_rate=100
|
||||
)
|
||||
|
||||
add_taxes(si)
|
||||
si.save()
|
||||
|
||||
|
@ -46,5 +46,5 @@ def validate_disabled(doc):
|
||||
frappe.throw(_("Disabled template must not be default template"))
|
||||
|
||||
def validate_for_tax_category(doc):
|
||||
if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0}):
|
||||
if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0, "name": ["!=", doc.name]}):
|
||||
frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))
|
||||
|
@ -229,7 +229,8 @@ def create_sales_invoice(**args):
|
||||
'qty': args.qty or 1,
|
||||
'rate': args.rate or 10000,
|
||||
'cost_center': 'Main - _TC',
|
||||
'expense_account': 'Cost of Goods Sold - _TC'
|
||||
'expense_account': 'Cost of Goods Sold - _TC',
|
||||
'warehouse': args.warehouse or '_Test Warehouse - _TC'
|
||||
}]
|
||||
})
|
||||
|
||||
|
@ -723,7 +723,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
|
||||
|
||||
make_stock_entry(target="_Test Warehouse - _TC",
|
||||
item_code="_Test Item Home Desktop 100", qty=10, basic_rate=100)
|
||||
item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100)
|
||||
make_stock_entry(target="_Test Warehouse - _TC",
|
||||
item_code = "Test Extra Item 1", qty=100, basic_rate=100)
|
||||
make_stock_entry(target="_Test Warehouse - _TC",
|
||||
|
@ -27,11 +27,17 @@
|
||||
"stock_qty",
|
||||
"sec_break1",
|
||||
"price_list_rate",
|
||||
"last_purchase_rate",
|
||||
"col_break3",
|
||||
"base_price_list_rate",
|
||||
"discount_and_margin_section",
|
||||
"margin_type",
|
||||
"margin_rate_or_amount",
|
||||
"rate_with_margin",
|
||||
"column_break_28",
|
||||
"discount_percentage",
|
||||
"discount_amount",
|
||||
"col_break3",
|
||||
"last_purchase_rate",
|
||||
"base_price_list_rate",
|
||||
"base_rate_with_margin",
|
||||
"sec_break2",
|
||||
"rate",
|
||||
"amount",
|
||||
@ -735,6 +741,7 @@
|
||||
"fieldname": "stock_uom_rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rate of Stock UOM",
|
||||
"no_copy": 1,
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
@ -747,9 +754,54 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "company_total_stock",
|
||||
"fieldtype": "Float",
|
||||
"label": "Available Qty at Company",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "discount_and_margin_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Discount and Margin"
|
||||
},
|
||||
{
|
||||
"depends_on": "price_list_rate",
|
||||
"fieldname": "margin_type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Margin Type",
|
||||
"options": "\nPercentage\nAmount",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.margin_type && doc.price_list_rate",
|
||||
"fieldname": "margin_rate_or_amount",
|
||||
"fieldtype": "Float",
|
||||
"label": "Margin Rate or Amount",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
|
||||
"fieldname": "rate_with_margin",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rate With Margin",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_28",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
|
||||
"fieldname": "base_rate_with_margin",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rate With Margin (Company Currency)",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
@ -757,7 +809,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-03-22 11:20:00.121296",
|
||||
"modified": "2021-03-22 11:46:12.357435",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order Item",
|
||||
|
@ -795,7 +795,7 @@ class init_landed_taxes_and_totals(object):
|
||||
for d in self.doc.get(self.tax_field):
|
||||
if d.account_currency == company_currency:
|
||||
d.exchange_rate = 1
|
||||
elif not d.exchange_rate or d.exchange_rate == 1 or self.doc.posting_date:
|
||||
elif not d.exchange_rate:
|
||||
d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
|
||||
account_currency=d.account_currency, company=self.doc.company)
|
||||
|
||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
import unittest, os, json
|
||||
from frappe.utils import cstr
|
||||
from frappe.utils import cstr, cint
|
||||
from erpnext.erpnext_integrations.connectors.shopify_connection import create_order
|
||||
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import make_item
|
||||
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer
|
||||
@ -13,9 +13,14 @@ from frappe.core.doctype.data_import.data_import import import_doc
|
||||
|
||||
|
||||
class ShopifySettings(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
cls.allow_negative_stock = cint(frappe.db.get_value('Stock Settings', None, 'allow_negative_stock'))
|
||||
if not cls.allow_negative_stock:
|
||||
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
|
||||
|
||||
# use the fixture data
|
||||
import_doc(frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json"))
|
||||
|
||||
@ -24,9 +29,15 @@ class ShopifySettings(unittest.TestCase):
|
||||
frappe.reload_doctype("Delivery Note")
|
||||
frappe.reload_doctype("Sales Invoice")
|
||||
|
||||
self.setup_shopify()
|
||||
cls.setup_shopify()
|
||||
|
||||
def setup_shopify(self):
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
if not cls.allow_negative_stock:
|
||||
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
|
||||
|
||||
@classmethod
|
||||
def setup_shopify(cls):
|
||||
shopify_settings = frappe.get_doc("Shopify Settings")
|
||||
shopify_settings.taxes = []
|
||||
|
||||
@ -56,21 +67,20 @@ class ShopifySettings(unittest.TestCase):
|
||||
"delivery_note_series": "DN-"
|
||||
}).save(ignore_permissions=True)
|
||||
|
||||
self.shopify_settings = shopify_settings
|
||||
cls.shopify_settings = shopify_settings
|
||||
|
||||
def test_order(self):
|
||||
### Create Customer ###
|
||||
# Create Customer
|
||||
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_customer.json")) as shopify_customer:
|
||||
shopify_customer = json.load(shopify_customer)
|
||||
create_customer(shopify_customer.get("customer"), self.shopify_settings)
|
||||
|
||||
### Create Item ###
|
||||
# Create Item
|
||||
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_item.json")) as shopify_item:
|
||||
shopify_item = json.load(shopify_item)
|
||||
make_item("_Test Warehouse - _TC", shopify_item.get("product"))
|
||||
|
||||
|
||||
### Create Order ###
|
||||
# Create Order
|
||||
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_order.json")) as shopify_order:
|
||||
shopify_order = json.load(shopify_order)
|
||||
|
||||
@ -80,17 +90,17 @@ class ShopifySettings(unittest.TestCase):
|
||||
|
||||
self.assertEqual(cstr(shopify_order.get("order").get("id")), sales_order.shopify_order_id)
|
||||
|
||||
#check for customer
|
||||
# Check for customer
|
||||
shopify_order_customer_id = cstr(shopify_order.get("order").get("customer").get("id"))
|
||||
sales_order_customer_id = frappe.get_value("Customer", sales_order.customer, "shopify_customer_id")
|
||||
|
||||
self.assertEqual(shopify_order_customer_id, sales_order_customer_id)
|
||||
|
||||
#check sales invoice
|
||||
# Check sales invoice
|
||||
sales_invoice = frappe.get_doc("Sales Invoice", {"shopify_order_id": sales_order.shopify_order_id})
|
||||
self.assertEqual(sales_invoice.rounded_total, sales_order.rounded_total)
|
||||
|
||||
#check delivery note
|
||||
# Check delivery note
|
||||
delivery_note_count = frappe.db.sql("""select count(*) from `tabDelivery Note`
|
||||
where shopify_order_id = %s""", sales_order.shopify_order_id)[0][0]
|
||||
|
||||
|
@ -8,6 +8,8 @@ import unittest
|
||||
from frappe.utils import nowdate
|
||||
from datetime import date
|
||||
|
||||
test_dependencies = ["Employee"]
|
||||
|
||||
class TestAttendanceRequest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
for doctype in ["Attendance Request", "Attendance"]:
|
||||
|
@ -10,6 +10,8 @@ from erpnext.hr.doctype.attendance_request.test_attendance_request import get_em
|
||||
from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
|
||||
from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
|
||||
|
||||
test_dependencies = ["Employee"]
|
||||
|
||||
class TestCompensatoryLeaveRequest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.sql(''' delete from `tabCompensatory Leave Request`''')
|
||||
|
@ -13,6 +13,7 @@
|
||||
"stop_birthday_reminders",
|
||||
"expense_approver_mandatory_in_expense_claim",
|
||||
"leave_settings",
|
||||
"send_leave_notification",
|
||||
"leave_approval_notification_template",
|
||||
"leave_status_notification_template",
|
||||
"role_allowed_to_create_backdated_leave_application",
|
||||
@ -69,15 +70,19 @@
|
||||
"label": "Leave Settings"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.send_leave_notification == 1",
|
||||
"fieldname": "leave_approval_notification_template",
|
||||
"fieldtype": "Link",
|
||||
"label": "Leave Approval Notification Template",
|
||||
"mandatory_depends_on": "eval: doc.send_leave_notification == 1",
|
||||
"options": "Email Template"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.send_leave_notification == 1",
|
||||
"fieldname": "leave_status_notification_template",
|
||||
"fieldtype": "Link",
|
||||
"label": "Leave Status Notification Template",
|
||||
"mandatory_depends_on": "eval: doc.send_leave_notification == 1",
|
||||
"options": "Email Template"
|
||||
},
|
||||
{
|
||||
@ -132,13 +137,19 @@
|
||||
"fieldname": "automatically_allocate_leaves_based_on_leave_policy",
|
||||
"fieldtype": "Check",
|
||||
"label": "Automatically Allocate Leaves Based On Leave Policy"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "send_leave_notification",
|
||||
"fieldtype": "Check",
|
||||
"label": "Send Leave Notification"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cog",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-02-25 12:31:14.947865",
|
||||
"modified": "2021-03-14 02:04:22.907159",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "HR Settings",
|
||||
|
@ -13,11 +13,21 @@ class TestJobApplicant(unittest.TestCase):
|
||||
|
||||
def create_job_applicant(**args):
|
||||
args = frappe._dict(args)
|
||||
job_applicant = frappe.get_doc({
|
||||
"doctype": "Job Applicant",
|
||||
|
||||
filters = {
|
||||
"applicant_name": args.applicant_name or "_Test Applicant",
|
||||
"email_id": args.email_id or "test_applicant@example.com",
|
||||
}
|
||||
|
||||
if frappe.db.exists("Job Applicant", filters):
|
||||
return frappe.get_doc("Job Applicant", filters)
|
||||
|
||||
job_applicant = frappe.get_doc({
|
||||
"doctype": "Job Applicant",
|
||||
"status": args.status or "Open"
|
||||
})
|
||||
|
||||
job_applicant.update(filters)
|
||||
job_applicant.save()
|
||||
|
||||
return job_applicant
|
@ -13,14 +13,15 @@ from erpnext.hr.doctype.staffing_plan.test_staffing_plan import make_company
|
||||
|
||||
class TestJobOffer(unittest.TestCase):
|
||||
def test_job_offer_creation_against_vacancies(self):
|
||||
create_staffing_plan(staffing_details=[{
|
||||
"designation": "Designer",
|
||||
frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
|
||||
job_applicant = create_job_applicant(email_id="test_job_offer@example.com")
|
||||
job_offer = create_job_offer(job_applicant=job_applicant.name, designation="UX Designer")
|
||||
|
||||
create_staffing_plan(name='Test No Vacancies', staffing_details=[{
|
||||
"designation": "UX Designer",
|
||||
"vacancies": 0,
|
||||
"estimated_cost_per_position": 5000
|
||||
}])
|
||||
frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
|
||||
job_applicant = create_job_applicant(email_id="test_job_offer@example.com")
|
||||
job_offer = create_job_offer(job_applicant=job_applicant.name, designation="Researcher")
|
||||
self.assertRaises(frappe.ValidationError, job_offer.submit)
|
||||
|
||||
# test creation of job offer when vacancies are not present
|
||||
|
@ -6,6 +6,10 @@ from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
||||
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation, expire_allocation
|
||||
|
||||
class TestLeaveAllocation(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
frappe.db.sql("delete from `tabLeave Period`")
|
||||
|
||||
def test_overlapping_allocation(self):
|
||||
frappe.db.sql("delete from `tabLeave Allocation`")
|
||||
|
||||
|
@ -40,7 +40,8 @@ class LeaveApplication(Document):
|
||||
def on_update(self):
|
||||
if self.status == "Open" and self.docstatus < 1:
|
||||
# notify leave approver about creation
|
||||
self.notify_leave_approver()
|
||||
if frappe.db.get_single_value("HR Settings", "send_leave_notification"):
|
||||
self.notify_leave_approver()
|
||||
|
||||
def on_submit(self):
|
||||
if self.status == "Open":
|
||||
@ -50,7 +51,8 @@ class LeaveApplication(Document):
|
||||
self.update_attendance()
|
||||
|
||||
# notify leave applier about approval
|
||||
self.notify_employee()
|
||||
if frappe.db.get_single_value("HR Settings", "send_leave_notification"):
|
||||
self.notify_employee()
|
||||
self.create_leave_ledger_entry()
|
||||
self.reload()
|
||||
|
||||
@ -60,7 +62,8 @@ class LeaveApplication(Document):
|
||||
def on_cancel(self):
|
||||
self.create_leave_ledger_entry(submit=False)
|
||||
# notify leave applier about cancellation
|
||||
self.notify_employee()
|
||||
if frappe.db.get_single_value("HR Settings", "send_leave_notification"):
|
||||
self.notify_employee()
|
||||
self.cancel_attendance()
|
||||
|
||||
def validate_applicable_after(self):
|
||||
|
@ -12,7 +12,7 @@ from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
||||
from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
|
||||
from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
|
||||
|
||||
test_dependencies = ["Leave Allocation", "Leave Block List"]
|
||||
test_dependencies = ["Leave Allocation", "Leave Block List", "Employee"]
|
||||
|
||||
_test_records = [
|
||||
{
|
||||
|
@ -9,6 +9,8 @@ from erpnext.hr.doctype.leave_application.test_leave_application import get_leav
|
||||
from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
|
||||
from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy
|
||||
|
||||
test_dependencies = ["Employee"]
|
||||
|
||||
class TestLeavePolicyAssignment(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -7,6 +7,8 @@ import frappe
|
||||
import unittest
|
||||
from frappe.utils import nowdate, add_days
|
||||
|
||||
test_dependencies = ["Shift Type"]
|
||||
|
||||
class TestShiftRequest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
for doctype in ["Shift Request", "Shift Assignment"]:
|
||||
|
8
erpnext/hr/doctype/shift_type/test_records.json
Normal file
8
erpnext/hr/doctype/shift_type/test_records.json
Normal file
@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"doctype": "Shift Type",
|
||||
"name": "Day Shift",
|
||||
"start_time": "9:00:00",
|
||||
"end_time": "18:00:00"
|
||||
}
|
||||
]
|
@ -7,14 +7,4 @@ import frappe
|
||||
import unittest
|
||||
|
||||
class TestShiftType(unittest.TestCase):
|
||||
def test_make_shift_type(self):
|
||||
if frappe.db.exists("Shift Type", "Day Shift"):
|
||||
return
|
||||
shift_type = frappe.get_doc({
|
||||
"doctype": "Shift Type",
|
||||
"name": "Day Shift",
|
||||
"start_time": "9:00:00",
|
||||
"end_time": "18:00:00"
|
||||
})
|
||||
shift_type.insert()
|
||||
|
||||
pass
|
||||
|
@ -39,6 +39,7 @@ class StaffingPlan(Document):
|
||||
detail.current_count = designation_counts['employee_count']
|
||||
detail.current_openings = designation_counts['job_openings']
|
||||
|
||||
detail.total_estimated_cost = 0
|
||||
if detail.number_of_positions > 0:
|
||||
if detail.vacancies > 0 and detail.estimated_cost_per_position:
|
||||
detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position)
|
||||
|
@ -91,6 +91,10 @@ def capture_razorpay_donations(*args, **kwargs):
|
||||
if not data.event == 'payment.captured':
|
||||
return
|
||||
|
||||
# to avoid capturing subscription payments as donations
|
||||
if payment.description and 'subscription' in str(payment.description).lower():
|
||||
return
|
||||
|
||||
donor = get_donor(payment.email)
|
||||
if not donor:
|
||||
donor = create_donor(payment)
|
||||
@ -119,7 +123,7 @@ def create_donation(donor, payment):
|
||||
'donor_name': donor.donor_name,
|
||||
'email': donor.email,
|
||||
'date': getdate(),
|
||||
'amount': flt(payment.amount),
|
||||
'amount': flt(payment.amount) / 100, # Convert to rupees from paise
|
||||
'mode_of_payment': payment.method,
|
||||
'razorpay_payment_id': payment.id
|
||||
}).insert(ignore_mandatory=True)
|
||||
|
@ -48,7 +48,7 @@ class Membership(Document):
|
||||
last_membership = erpnext.get_last_membership(self.member)
|
||||
|
||||
# if person applied for offline membership
|
||||
if last_membership and not frappe.session.user == "Administrator":
|
||||
if last_membership and last_membership.name != self.name and not frappe.session.user == "Administrator":
|
||||
# if last membership does not expire in 30 days, then do not allow to renew
|
||||
if getdate(add_days(last_membership.to_date, -30)) > getdate(nowdate()) :
|
||||
frappe.throw(_("You can only renew if your membership expires within 30 days"))
|
||||
@ -90,6 +90,7 @@ class Membership(Document):
|
||||
self.validate_membership_type_and_settings(plan, settings)
|
||||
|
||||
invoice = make_invoice(self, member, plan, settings)
|
||||
self.reload()
|
||||
self.invoice = invoice.name
|
||||
|
||||
if with_payment_entry:
|
||||
@ -284,10 +285,11 @@ def trigger_razorpay_subscription(*args, **kwargs):
|
||||
|
||||
settings = frappe.get_doc("Non Profit Settings")
|
||||
if settings.allow_invoicing and settings.automate_membership_invoicing:
|
||||
membership.reload()
|
||||
membership.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
|
||||
|
||||
except Exception as e:
|
||||
message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), __("Payment ID"), payment.id)
|
||||
message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), _("Payment ID"), payment.id)
|
||||
log = frappe.log_error(message, _("Error creating membership entry for {0}").format(member.name))
|
||||
notify_failure(log)
|
||||
return { "status": "Failed", "reason": e}
|
||||
|
@ -759,3 +759,4 @@ erpnext.patches.v13_0.update_vehicle_no_reqd_condition
|
||||
erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
|
||||
erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
|
||||
erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
|
||||
execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
|
||||
|
@ -11,6 +11,7 @@ def execute():
|
||||
|
||||
# Update options in gst_state custom fields
|
||||
for field in custom_fields:
|
||||
gst_state_field = frappe.get_doc('Custom Field', field)
|
||||
gst_state_field.options = '\n'.join(states)
|
||||
gst_state_field.save()
|
||||
if frappe.db.exists('Custom Field', field):
|
||||
gst_state_field = frappe.get_doc('Custom Field', field)
|
||||
gst_state_field.options = '\n'.join(states)
|
||||
gst_state_field.save()
|
||||
|
@ -2,15 +2,12 @@ import frappe
|
||||
from erpnext.regional.india.setup import make_custom_fields
|
||||
|
||||
def execute():
|
||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||
if not company:
|
||||
return
|
||||
if frappe.get_all('Company', filters = {'country': 'India'}):
|
||||
make_custom_fields()
|
||||
|
||||
make_custom_fields()
|
||||
|
||||
if not frappe.db.exists('Party Type', 'Donor'):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Party Type',
|
||||
'party_type': 'Donor',
|
||||
'account_type': 'Receivable'
|
||||
}).insert(ignore_permissions=True)
|
||||
if not frappe.db.exists('Party Type', 'Donor'):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Party Type',
|
||||
'party_type': 'Donor',
|
||||
'account_type': 'Receivable'
|
||||
}).insert(ignore_permissions=True)
|
||||
|
@ -9,17 +9,10 @@ from frappe import _, bold
|
||||
from frappe.utils import getdate, date_diff, comma_and, formatdate
|
||||
|
||||
class AdditionalSalary(Document):
|
||||
|
||||
def on_submit(self):
|
||||
if self.ref_doctype == "Employee Advance" and self.ref_docname:
|
||||
frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", self.amount)
|
||||
|
||||
def before_insert(self):
|
||||
if frappe.db.exists("Additional Salary", {"employee": self.employee, "salary_component": self.salary_component,
|
||||
"amount": self.amount, "payroll_date": self.payroll_date, "company": self.company, "docstatus": 1}):
|
||||
|
||||
frappe.throw(_("Additional Salary Component Exists."))
|
||||
|
||||
def validate(self):
|
||||
self.validate_dates()
|
||||
self.validate_salary_structure()
|
||||
@ -89,10 +82,11 @@ class AdditionalSalary(Document):
|
||||
no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
|
||||
return amount_per_day * no_of_days
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_additional_salary_component(employee, start_date, end_date, component_type):
|
||||
additional_salaries = frappe.db.sql("""
|
||||
select name, salary_component, type, amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
|
||||
def get_additional_salaries(employee, start_date, end_date, component_type):
|
||||
additional_salary_list = frappe.db.sql("""
|
||||
select name, salary_component as component, type, amount,
|
||||
overwrite_salary_structure_amount as overwrite,
|
||||
deduct_full_tax_on_selected_payroll_date
|
||||
from `tabAdditional Salary`
|
||||
where employee=%(employee)s
|
||||
and docstatus = 1
|
||||
@ -102,7 +96,7 @@ def get_additional_salary_component(employee, start_date, end_date, component_ty
|
||||
from_date <= %(to_date)s and to_date >= %(to_date)s
|
||||
)
|
||||
and type = %(component_type)s
|
||||
order by salary_component, overwrite_salary_structure_amount DESC
|
||||
order by salary_component, overwrite ASC
|
||||
""", {
|
||||
'employee': employee,
|
||||
'from_date': start_date,
|
||||
@ -110,38 +104,18 @@ def get_additional_salary_component(employee, start_date, end_date, component_ty
|
||||
'component_type': "Earning" if component_type == "earnings" else "Deduction"
|
||||
}, as_dict=1)
|
||||
|
||||
existing_salary_components= []
|
||||
salary_components_details = {}
|
||||
additional_salary_details = []
|
||||
additional_salaries = []
|
||||
components_to_overwrite = []
|
||||
|
||||
overwrites_components = [ele.salary_component for ele in additional_salaries if ele.overwrite_salary_structure_amount == 1]
|
||||
for d in additional_salary_list:
|
||||
if d.overwrite:
|
||||
if d.component in components_to_overwrite:
|
||||
frappe.throw(_("Multiple Additional Salaries with overwrite "
|
||||
"property exist for Salary Component {0} between {1} and {2}.").format(
|
||||
frappe.bold(d.component), start_date, end_date), title=_("Error"))
|
||||
|
||||
component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
|
||||
for d in additional_salaries:
|
||||
components_to_overwrite.append(d.component)
|
||||
|
||||
if d.salary_component not in existing_salary_components:
|
||||
component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
|
||||
struct_row = frappe._dict({'salary_component': d.salary_component})
|
||||
if component:
|
||||
struct_row.update(component[0])
|
||||
additional_salaries.append(d)
|
||||
|
||||
struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
|
||||
struct_row['is_additional_component'] = 1
|
||||
|
||||
salary_components_details[d.salary_component] = struct_row
|
||||
|
||||
|
||||
if overwrites_components.count(d.salary_component) > 1:
|
||||
frappe.throw(_("Multiple Additional Salaries with overwrite property exist for Salary Component: {0} between {1} and {2}.".format(d.salary_component, start_date, end_date)), title=_("Error"))
|
||||
else:
|
||||
additional_salary_details.append({
|
||||
'name': d.name,
|
||||
'component': d.salary_component,
|
||||
'amount': d.amount,
|
||||
'type': d.type,
|
||||
'overwrite': d.overwrite_salary_structure_amount,
|
||||
})
|
||||
|
||||
existing_salary_components.append(d.salary_component)
|
||||
|
||||
return salary_components_details, additional_salary_details
|
||||
return additional_salaries
|
||||
|
@ -15,9 +15,12 @@ from frappe.utils import getdate, add_days, get_datetime, flt
|
||||
|
||||
test_dependencies = ["Salary Component", "Salary Slip", "Account"]
|
||||
class TestGratuity(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
make_earning_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
|
||||
make_deduction_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
|
||||
|
||||
def setUp(self):
|
||||
frappe.db.sql("DELETE FROM `tabGratuity`")
|
||||
frappe.db.sql("DELETE FROM `tabAdditional Salary` WHERE ref_doctype = 'Gratuity'")
|
||||
|
||||
|
@ -12,7 +12,7 @@ from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import get_salary_component_account, \
|
||||
make_earning_salary_component, make_deduction_salary_component, create_account, make_employee_salary_slip
|
||||
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure, create_salary_structure_assignment
|
||||
from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry
|
||||
from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry, create_loan_type, create_loan_accounts
|
||||
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
|
||||
|
||||
class TestPayrollEntry(unittest.TestCase):
|
||||
@ -168,15 +168,23 @@ class TestPayrollEntry(unittest.TestCase):
|
||||
salary_structure = "Test Salary Structure for Loan"
|
||||
make_salary_structure(salary_structure, "Monthly", employee=employee_doc.name, company="_Test Company", currency=company_doc.default_currency)
|
||||
|
||||
if not frappe.db.exists("Loan Type", "Car Loan"):
|
||||
create_loan_accounts()
|
||||
create_loan_type("Car Loan", 500000, 8.4,
|
||||
is_term_loan=1,
|
||||
mode_of_payment='Cash',
|
||||
payment_account='Payment Account - _TC',
|
||||
loan_account='Loan Account - _TC',
|
||||
interest_income_account='Interest Income Account - _TC',
|
||||
penalty_income_account='Penalty Income Account - _TC')
|
||||
|
||||
loan = create_loan(applicant, "Car Loan", 280000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
|
||||
loan.repay_from_salary = 1
|
||||
loan.submit()
|
||||
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
|
||||
|
||||
process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
|
||||
|
||||
|
||||
dates = get_start_end_dates('Monthly', nowdate())
|
||||
make_payroll_entry(company="_Test Company", start_date=dates.start_date, payable_account=company_doc.default_payroll_payable_account,
|
||||
currency=company_doc.default_currency, end_date=dates.end_date, branch=branch, cost_center="Main - _TC", payment_account="Cash - _TC")
|
||||
|
@ -13,7 +13,7 @@ from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_da
|
||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salary_component
|
||||
from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
|
||||
from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
|
||||
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
|
||||
from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
|
||||
@ -524,7 +524,7 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
except NameError as err:
|
||||
frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
|
||||
title=_("Name error"))
|
||||
title=_("Name error"))
|
||||
except SyntaxError as err:
|
||||
frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
|
||||
except Exception as e:
|
||||
@ -558,15 +558,16 @@ class SalarySlip(TransactionBase):
|
||||
self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
|
||||
|
||||
def add_additional_salary_components(self, component_type):
|
||||
salary_components_details, additional_salary_details = get_additional_salary_component(self.employee,
|
||||
additional_salaries = get_additional_salaries(self.employee,
|
||||
self.start_date, self.end_date, component_type)
|
||||
if salary_components_details and additional_salary_details:
|
||||
for additional_salary in additional_salary_details:
|
||||
additional_salary =frappe._dict(additional_salary)
|
||||
amount = additional_salary.amount
|
||||
overwrite = additional_salary.overwrite
|
||||
self.update_component_row(frappe._dict(salary_components_details[additional_salary.component]), amount,
|
||||
component_type, overwrite=overwrite, additional_salary=additional_salary.name)
|
||||
|
||||
for additional_salary in additional_salaries:
|
||||
self.update_component_row(
|
||||
get_salary_component_data(additional_salary.component),
|
||||
additional_salary.amount,
|
||||
component_type,
|
||||
additional_salary
|
||||
)
|
||||
|
||||
def add_tax_components(self, payroll_period):
|
||||
# Calculate variable_based_on_taxable_salary after all components updated in salary slip
|
||||
@ -583,47 +584,59 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
for d in tax_components:
|
||||
tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period)
|
||||
tax_row = self.get_salary_slip_row(d)
|
||||
tax_row = get_salary_component_data(d)
|
||||
self.update_component_row(tax_row, tax_amount, "deductions")
|
||||
|
||||
def update_component_row(self, struct_row, amount, key, overwrite=1, additional_salary = ''):
|
||||
def update_component_row(self, component_data, amount, component_type, additional_salary=None):
|
||||
component_row = None
|
||||
for d in self.get(key):
|
||||
if d.salary_component == struct_row.salary_component:
|
||||
for d in self.get(component_type):
|
||||
if d.salary_component != component_data.salary_component:
|
||||
continue
|
||||
|
||||
if (
|
||||
not d.additional_salary
|
||||
and (not additional_salary or additional_salary.overwrite)
|
||||
or additional_salary
|
||||
and additional_salary.name == d.additional_salary
|
||||
):
|
||||
component_row = d
|
||||
break
|
||||
|
||||
if not component_row or (struct_row.get("is_additional_component") and not overwrite):
|
||||
if amount:
|
||||
self.append(key, {
|
||||
'amount': amount,
|
||||
'default_amount': amount if not struct_row.get("is_additional_component") else 0,
|
||||
'depends_on_payment_days' : struct_row.depends_on_payment_days,
|
||||
'salary_component' : struct_row.salary_component,
|
||||
'abbr' : struct_row.abbr or struct_row.get("salary_component_abbr"),
|
||||
'additional_salary': additional_salary,
|
||||
'do_not_include_in_total' : struct_row.do_not_include_in_total,
|
||||
'is_tax_applicable': struct_row.is_tax_applicable,
|
||||
'is_flexible_benefit': struct_row.is_flexible_benefit,
|
||||
'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary,
|
||||
'deduct_full_tax_on_selected_payroll_date': struct_row.deduct_full_tax_on_selected_payroll_date,
|
||||
'additional_amount': amount if struct_row.get("is_additional_component") else 0,
|
||||
'exempted_from_income_tax': struct_row.exempted_from_income_tax
|
||||
})
|
||||
if additional_salary and additional_salary.overwrite:
|
||||
# Additional Salary with overwrite checked, remove default rows of same component
|
||||
self.set(component_type, [
|
||||
d for d in self.get(component_type)
|
||||
if d.salary_component != component_data.salary_component
|
||||
or d.additional_salary and additional_salary.name != d.additional_salary
|
||||
or d == component_row
|
||||
])
|
||||
|
||||
if not component_row:
|
||||
if not amount:
|
||||
return
|
||||
|
||||
component_row = self.append(component_type)
|
||||
for attr in (
|
||||
'depends_on_payment_days', 'salary_component', 'abbr'
|
||||
'do_not_include_in_total', 'is_tax_applicable',
|
||||
'is_flexible_benefit', 'variable_based_on_taxable_salary',
|
||||
'exempted_from_income_tax'
|
||||
):
|
||||
component_row.set(attr, component_data.get(attr))
|
||||
|
||||
if additional_salary:
|
||||
component_row.default_amount = 0
|
||||
component_row.additional_amount = amount
|
||||
component_row.additional_salary = additional_salary.name
|
||||
component_row.deduct_full_tax_on_selected_payroll_date = \
|
||||
additional_salary.deduct_full_tax_on_selected_payroll_date
|
||||
else:
|
||||
if struct_row.get("is_additional_component"):
|
||||
if overwrite:
|
||||
component_row.additional_amount = amount - component_row.get("default_amount", 0)
|
||||
component_row.additional_salary = additional_salary
|
||||
else:
|
||||
component_row.additional_amount = amount
|
||||
component_row.default_amount = amount
|
||||
component_row.additional_amount = 0
|
||||
component_row.deduct_full_tax_on_selected_payroll_date = \
|
||||
component_data.deduct_full_tax_on_selected_payroll_date
|
||||
|
||||
if not overwrite and component_row.default_amount:
|
||||
amount += component_row.default_amount
|
||||
else:
|
||||
component_row.default_amount = amount
|
||||
|
||||
component_row.amount = amount
|
||||
component_row.deduct_full_tax_on_selected_payroll_date = struct_row.deduct_full_tax_on_selected_payroll_date
|
||||
component_row.amount = amount
|
||||
|
||||
def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period):
|
||||
if not payroll_period:
|
||||
@ -950,26 +963,13 @@ class SalarySlip(TransactionBase):
|
||||
return frappe.safe_eval(condition, self.whitelisted_globals, data)
|
||||
except NameError as err:
|
||||
frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
|
||||
title=_("Name error"))
|
||||
title=_("Name error"))
|
||||
except SyntaxError as err:
|
||||
frappe.throw(_("Syntax error in condition: {0}").format(err))
|
||||
except Exception as e:
|
||||
frappe.throw(_("Error in formula or condition: {0}").format(e))
|
||||
raise
|
||||
|
||||
def get_salary_slip_row(self, salary_component):
|
||||
component = frappe.get_doc("Salary Component", salary_component)
|
||||
# Data for update_component_row
|
||||
struct_row = frappe._dict()
|
||||
struct_row['depends_on_payment_days'] = component.depends_on_payment_days
|
||||
struct_row['salary_component'] = component.name
|
||||
struct_row['abbr'] = component.salary_component_abbr
|
||||
struct_row['do_not_include_in_total'] = component.do_not_include_in_total
|
||||
struct_row['is_tax_applicable'] = component.is_tax_applicable
|
||||
struct_row['is_flexible_benefit'] = component.is_flexible_benefit
|
||||
struct_row['variable_based_on_taxable_salary'] = component.variable_based_on_taxable_salary
|
||||
return struct_row
|
||||
|
||||
def get_component_totals(self, component_type, depends_on_payment_days=0):
|
||||
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
||||
["date_of_joining", "relieving_date"])
|
||||
@ -1032,7 +1032,6 @@ class SalarySlip(TransactionBase):
|
||||
self.total_loan_repayment += payment.total_payment
|
||||
|
||||
def get_loan_details(self):
|
||||
|
||||
return frappe.get_all("Loan",
|
||||
fields=["name", "interest_income_account", "loan_account", "loan_type"],
|
||||
filters = {
|
||||
@ -1263,3 +1262,19 @@ def unlink_ref_doc_from_salary_slip(ref_no):
|
||||
def generate_password_for_pdf(policy_template, employee):
|
||||
employee = frappe.get_doc("Employee", employee)
|
||||
return policy_template.format(**employee.as_dict())
|
||||
|
||||
def get_salary_component_data(component):
|
||||
return frappe.get_value(
|
||||
"Salary Component",
|
||||
component,
|
||||
[
|
||||
"name as salary_component",
|
||||
"depends_on_payment_days",
|
||||
"salary_component_abbr as abbr",
|
||||
"do_not_include_in_total",
|
||||
"is_tax_applicable",
|
||||
"is_flexible_benefit",
|
||||
"variable_based_on_taxable_salary",
|
||||
],
|
||||
as_dict=1,
|
||||
)
|
||||
|
@ -361,7 +361,6 @@ class TestSalarySlip(unittest.TestCase):
|
||||
# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
|
||||
frappe.db.sql("""delete from `tabPayroll Period`""")
|
||||
frappe.db.sql("""delete from `tabSalary Component`""")
|
||||
frappe.db.sql("""delete from `tabAdditional Salary`""")
|
||||
|
||||
payroll_period = create_payroll_period()
|
||||
|
||||
|
@ -81,12 +81,18 @@ class Project(Document):
|
||||
|
||||
def calculate_start_date(self, task_details):
|
||||
self.start_date = add_days(self.expected_start_date, task_details.start)
|
||||
self.start_date = update_if_holiday(self.holiday_list, self.start_date)
|
||||
self.start_date = self.update_if_holiday(self.start_date)
|
||||
return self.start_date
|
||||
|
||||
def calculate_end_date(self, task_details):
|
||||
self.end_date = add_days(self.start_date, task_details.duration)
|
||||
return update_if_holiday(self.holiday_list, self.end_date)
|
||||
return self.update_if_holiday(self.end_date)
|
||||
|
||||
def update_if_holiday(self, date):
|
||||
holiday_list = self.holiday_list or get_holiday_list(self.company)
|
||||
while is_holiday(holiday_list, date):
|
||||
date = add_days(date, 1)
|
||||
return date
|
||||
|
||||
def dependency_mapping(self, template_tasks, project_tasks):
|
||||
for template_task in template_tasks:
|
||||
@ -541,9 +547,3 @@ def set_project_status(project, status):
|
||||
|
||||
project.status = status
|
||||
project.save()
|
||||
|
||||
def update_if_holiday(holiday_list, date):
|
||||
holiday_list = holiday_list or get_holiday_list()
|
||||
while is_holiday(holiday_list, date):
|
||||
date = add_days(date, 1)
|
||||
return date
|
||||
|
@ -8,7 +8,6 @@ test_records = frappe.get_test_records('Project')
|
||||
test_ignore = ["Sales Order"]
|
||||
|
||||
from erpnext.projects.doctype.project_template.test_project_template import make_project_template
|
||||
from erpnext.projects.doctype.project.project import update_if_holiday
|
||||
from erpnext.projects.doctype.task.test_task import create_task
|
||||
from frappe.utils import getdate, nowdate, add_days
|
||||
|
||||
@ -97,7 +96,8 @@ def get_project(name, template):
|
||||
project_name = name,
|
||||
status = 'Open',
|
||||
project_template = template.name,
|
||||
expected_start_date = nowdate()
|
||||
expected_start_date = nowdate(),
|
||||
company="_Test Company"
|
||||
)).insert()
|
||||
|
||||
return project
|
||||
@ -131,7 +131,7 @@ def task_exists(subject):
|
||||
|
||||
def calculate_end_date(project, start, duration):
|
||||
start = add_days(project.expected_start_date, start)
|
||||
start = update_if_holiday(project.holiday_list, start)
|
||||
start = project.update_if_holiday(start)
|
||||
end = add_days(start, duration)
|
||||
end = update_if_holiday(project.holiday_list, end)
|
||||
end = project.update_if_holiday(end)
|
||||
return getdate(end)
|
@ -13,9 +13,18 @@ from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.payroll.doctype.salary_structure.test_salary_structure \
|
||||
import make_salary_structure, create_salary_structure_assignment
|
||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
||||
make_earning_salary_component,
|
||||
make_deduction_salary_component
|
||||
)
|
||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||
|
||||
class TestTimesheet(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
make_earning_salary_component(setup=True, company_list=['_Test Company'])
|
||||
make_deduction_salary_component(setup=True, company_list=['_Test Company'])
|
||||
|
||||
def setUp(self):
|
||||
for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
|
||||
frappe.db.sql("delete from `tab%s`" % dt)
|
||||
@ -49,7 +58,7 @@ class TestTimesheet(unittest.TestCase):
|
||||
self.assertEqual(timesheet.total_billable_amount, 0)
|
||||
|
||||
def test_salary_slip_from_timesheet(self):
|
||||
emp = make_employee("test_employee_6@salary.com")
|
||||
emp = make_employee("test_employee_6@salary.com", company="_Test Company")
|
||||
|
||||
salary_structure = make_salary_structure_for_timesheet(emp)
|
||||
timesheet = make_timesheet(emp, simulate = True, billable=1)
|
||||
|
@ -349,13 +349,12 @@ class GSTR3BReport(Document):
|
||||
return inter_state_supply_details
|
||||
|
||||
def get_inward_nil_exempt(self, state):
|
||||
|
||||
inward_nil_exempt = frappe.db.sql(""" select p.place_of_supply, sum(i.base_amount) as base_amount,
|
||||
i.is_nil_exempt, i.is_non_gst from `tabPurchase Invoice` p , `tabPurchase Invoice Item` i
|
||||
where p.docstatus = 1 and p.name = i.parent
|
||||
and i.is_nil_exempt = 1 or i.is_non_gst = 1 and
|
||||
and (i.is_nil_exempt = 1 or i.is_non_gst = 1) and
|
||||
month(p.posting_date) = %s and year(p.posting_date) = %s and p.company = %s and p.company_gstin = %s
|
||||
group by p.place_of_supply """, (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
|
||||
group by p.place_of_supply, i.is_nil_exempt, i.is_non_gst""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
|
||||
|
||||
inward_nil_exempt_details = {
|
||||
"gst": {
|
||||
|
@ -16,6 +16,7 @@ class TaxExemption80GCertificate(Document):
|
||||
self.validate_duplicates()
|
||||
self.validate_company_details()
|
||||
self.set_company_address()
|
||||
self.calculate_total()
|
||||
self.set_title()
|
||||
|
||||
def validate_date(self):
|
||||
@ -29,7 +30,10 @@ class TaxExemption80GCertificate(Document):
|
||||
|
||||
def validate_duplicates(self):
|
||||
if self.recipient == 'Donor':
|
||||
certificate = frappe.db.exists(self.doctype, {'donation': self.donation})
|
||||
certificate = frappe.db.exists(self.doctype, {
|
||||
'donation': self.donation,
|
||||
'name': ('!=', self.name)
|
||||
})
|
||||
if certificate:
|
||||
frappe.throw(_('An 80G Certificate {0} already exists for the donation {1}').format(
|
||||
get_link_to_form(self.doctype, certificate), frappe.bold(self.donation)
|
||||
@ -51,8 +55,17 @@ class TaxExemption80GCertificate(Document):
|
||||
self.company_address = address.company_address
|
||||
self.company_address_display = address.company_address_display
|
||||
|
||||
def calculate_total(self):
|
||||
if self.recipient == 'Donor':
|
||||
return
|
||||
|
||||
total = 0
|
||||
for entry in self.payments:
|
||||
total += flt(entry.amount)
|
||||
self.total = total
|
||||
|
||||
def set_title(self):
|
||||
if self.recipient == "Member":
|
||||
if self.recipient == 'Member':
|
||||
self.title = self.member_name
|
||||
else:
|
||||
self.title = self.donor_name
|
||||
|
@ -719,25 +719,12 @@ def update_grand_total_for_rcm(doc, method):
|
||||
if country != 'India':
|
||||
return
|
||||
|
||||
if not doc.total_taxes_and_charges:
|
||||
gst_tax, base_gst_tax = get_gst_tax_amount(doc)
|
||||
|
||||
if not base_gst_tax:
|
||||
return
|
||||
|
||||
if doc.reverse_charge == 'Y':
|
||||
gst_accounts = get_gst_accounts(doc.company)
|
||||
gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
|
||||
+ gst_accounts.get('igst_account')
|
||||
|
||||
base_gst_tax = 0
|
||||
gst_tax = 0
|
||||
|
||||
for tax in doc.get('taxes'):
|
||||
if tax.category not in ("Total", "Valuation and Total"):
|
||||
continue
|
||||
|
||||
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list:
|
||||
base_gst_tax += tax.base_tax_amount_after_discount_amount
|
||||
gst_tax += tax.tax_amount_after_discount_amount
|
||||
|
||||
doc.taxes_and_charges_added -= gst_tax
|
||||
doc.total_taxes_and_charges -= gst_tax
|
||||
doc.base_taxes_and_charges_added -= base_gst_tax
|
||||
@ -771,6 +758,11 @@ def make_regional_gl_entries(gl_entries, doc):
|
||||
if country != 'India':
|
||||
return gl_entries
|
||||
|
||||
gst_tax, base_gst_tax = get_gst_tax_amount(doc)
|
||||
|
||||
if not base_gst_tax:
|
||||
return gl_entries
|
||||
|
||||
if doc.reverse_charge == 'Y':
|
||||
gst_accounts = get_gst_accounts(doc.company)
|
||||
gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
|
||||
@ -799,6 +791,24 @@ def make_regional_gl_entries(gl_entries, doc):
|
||||
|
||||
return gl_entries
|
||||
|
||||
def get_gst_tax_amount(doc):
|
||||
gst_accounts = get_gst_accounts(doc.company)
|
||||
gst_account_list = gst_accounts.get('cgst_account', []) + gst_accounts.get('sgst_account', []) \
|
||||
+ gst_accounts.get('igst_account', [])
|
||||
|
||||
base_gst_tax = 0
|
||||
gst_tax = 0
|
||||
|
||||
for tax in doc.get('taxes'):
|
||||
if tax.category not in ("Total", "Valuation and Total"):
|
||||
continue
|
||||
|
||||
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list:
|
||||
base_gst_tax += tax.base_tax_amount_after_discount_amount
|
||||
gst_tax += tax.tax_amount_after_discount_amount
|
||||
|
||||
return gst_tax, base_gst_tax
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_regional_round_off_accounts(company, account_list):
|
||||
country = frappe.get_cached_value('Company', company, 'country')
|
||||
|
@ -1,11 +1,12 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json
|
||||
from frappe.utils import flt, add_days, nowdate
|
||||
import frappe.permissions
|
||||
import unittest
|
||||
import frappe
|
||||
import frappe.permissions
|
||||
from frappe.utils import flt, add_days, nowdate
|
||||
from frappe.core.doctype.user_permission.test_user_permission import create_user
|
||||
from erpnext.selling.doctype.sales_order.sales_order \
|
||||
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
@ -444,10 +445,8 @@ class TestSalesOrder(unittest.TestCase):
|
||||
def test_update_child_perm(self):
|
||||
so = make_sales_order(item_code= "_Test Item", qty=4)
|
||||
|
||||
user = 'test@example.com'
|
||||
test_user = frappe.get_doc('User', user)
|
||||
test_user.add_roles("Accounts User")
|
||||
frappe.set_user(user)
|
||||
test_user = create_user("test_so_child_perms@example.com", "Accounts User")
|
||||
frappe.set_user(test_user.name)
|
||||
|
||||
# update qty
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}])
|
||||
@ -456,18 +455,14 @@ class TestSalesOrder(unittest.TestCase):
|
||||
# add new item
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
|
||||
test_user.remove_roles("Accounts User")
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
def test_update_child_qty_rate_with_workflow(self):
|
||||
from frappe.model.workflow import apply_workflow
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
workflow = make_sales_order_workflow()
|
||||
so = make_sales_order(item_code= "_Test Item", qty=1, rate=150, do_not_submit=1)
|
||||
apply_workflow(so, 'Approve')
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
user = 'test@example.com'
|
||||
test_user = frappe.get_doc('User', user)
|
||||
test_user.add_roles("Sales User", "Test Junior Approver")
|
||||
@ -618,33 +613,31 @@ class TestSalesOrder(unittest.TestCase):
|
||||
frappe.db.set_value("Stock Settings", None, "default_warehouse", old_stock_settings_value)
|
||||
|
||||
def test_warehouse_user(self):
|
||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
|
||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
|
||||
frappe.permissions.add_user_permission("Company", "_Test Company 1", "test2@example.com")
|
||||
|
||||
test_user = frappe.get_doc("User", "test@example.com")
|
||||
test_user.add_roles("Sales User", "Stock User")
|
||||
test_user.remove_roles("Sales Manager")
|
||||
test_user = create_user("test_so_warehouse_user@example.com", "Sales User", "Stock User")
|
||||
|
||||
test_user_2 = frappe.get_doc("User", "test2@example.com")
|
||||
test_user_2.add_roles("Sales User", "Stock User")
|
||||
test_user_2.remove_roles("Sales Manager")
|
||||
|
||||
frappe.set_user("test@example.com")
|
||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", test_user.name)
|
||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", test_user_2.name)
|
||||
frappe.permissions.add_user_permission("Company", "_Test Company 1", test_user_2.name)
|
||||
|
||||
so = make_sales_order(company="_Test Company 1",
|
||||
frappe.set_user(test_user.name)
|
||||
|
||||
so = make_sales_order(company="_Test Company 1", customer="_Test Customer 1",
|
||||
warehouse="_Test Warehouse 2 - _TC1", do_not_save=True)
|
||||
so.conversion_rate = 0.02
|
||||
so.plc_conversion_rate = 0.02
|
||||
self.assertRaises(frappe.PermissionError, so.insert)
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
frappe.set_user(test_user_2.name)
|
||||
so.insert()
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
|
||||
frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
|
||||
frappe.permissions.remove_user_permission("Company", "_Test Company 1", "test2@example.com")
|
||||
frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 1 - _TC", test_user.name)
|
||||
frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", test_user_2.name)
|
||||
frappe.permissions.remove_user_permission("Company", "_Test Company 1", test_user_2.name)
|
||||
|
||||
def test_block_delivery_note_against_cancelled_sales_order(self):
|
||||
so = make_sales_order()
|
||||
|
@ -93,6 +93,10 @@ erpnext.PointOfSale.Controller = class {
|
||||
})
|
||||
return frappe.utils.play_sound("error");
|
||||
}
|
||||
|
||||
// filter balance details for empty rows
|
||||
balance_details = balance_details.filter(d => d.mode_of_payment);
|
||||
|
||||
const method = "erpnext.selling.page.point_of_sale.point_of_sale.create_opening_voucher";
|
||||
const res = await frappe.call({ method, args: { pos_profile, company, balance_details }, freeze:true });
|
||||
!res.exc && me.prepare_app_defaults(res.message);
|
||||
|
@ -214,7 +214,7 @@
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"max_attachments": 3,
|
||||
"modified": "2021-02-08 17:02:44.951572",
|
||||
"modified": "2021-02-18 13:40:30.049650",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Item Group",
|
||||
@ -277,7 +277,7 @@
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"report": 1,
|
||||
"role": "Customer",
|
||||
"role": "All",
|
||||
"select": 1,
|
||||
"share": 1
|
||||
}
|
||||
|
@ -161,5 +161,4 @@ def add_standard_navbar_items():
|
||||
navbar_settings.save()
|
||||
|
||||
def add_app_name():
|
||||
settings = frappe.get_doc("System Settings")
|
||||
settings.app_name = _("ERPNext")
|
||||
frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
|
||||
|
@ -10,13 +10,14 @@
|
||||
"hide_custom": 0,
|
||||
"icon": "getting-started",
|
||||
"idx": 0,
|
||||
"is_default": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Home",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Healthcare",
|
||||
"label": "Accounting",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
@ -24,8 +25,8 @@
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Patient",
|
||||
"link_to": "Patient",
|
||||
"label": "Chart of Accounts",
|
||||
"link_to": "Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
@ -34,25 +35,8 @@
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Diagnosis",
|
||||
"link_to": "Diagnosis",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Agriculture",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Crop",
|
||||
"link_to": "Crop",
|
||||
"label": "Company",
|
||||
"link_to": "Company",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
@ -61,8 +45,8 @@
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Crop Cycle",
|
||||
"link_to": "Crop Cycle",
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
@ -71,112 +55,8 @@
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Location",
|
||||
"link_to": "Location",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Fertilizer",
|
||||
"link_to": "Fertilizer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Education",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Student",
|
||||
"link_to": "Student",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Course",
|
||||
"link_to": "Course",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Instructor",
|
||||
"link_to": "Instructor",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Room",
|
||||
"link_to": "Room",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Non Profit",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Member",
|
||||
"link_to": "Member",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Volunteer",
|
||||
"link_to": "Volunteer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Chapter",
|
||||
"link_to": "Chapter",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Donor",
|
||||
"link_to": "Donor",
|
||||
"label": "Supplier",
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
@ -188,6 +68,16 @@
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@ -302,73 +192,6 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounting",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Supplier",
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Company",
|
||||
"link_to": "Company",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Chart of Accounts",
|
||||
"link_to": "Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Opening Invoice Creation Tool",
|
||||
"link_to": "Opening Invoice Creation Tool",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@ -386,6 +209,16 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Opening Invoice Creation Tool",
|
||||
"link_to": "Opening Invoice Creation Tool",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@ -415,9 +248,177 @@
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Healthcare",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Patient",
|
||||
"link_to": "Patient",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Diagnosis",
|
||||
"link_to": "Diagnosis",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Education",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Student",
|
||||
"link_to": "Student",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Instructor",
|
||||
"link_to": "Instructor",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Course",
|
||||
"link_to": "Course",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Room",
|
||||
"link_to": "Room",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Non Profit",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Donor",
|
||||
"link_to": "Donor",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Member",
|
||||
"link_to": "Member",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Volunteer",
|
||||
"link_to": "Volunteer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Chapter",
|
||||
"link_to": "Chapter",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Agriculture",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Location",
|
||||
"link_to": "Location",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Crop",
|
||||
"link_to": "Crop",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Crop Cycle",
|
||||
"link_to": "Crop Cycle",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Fertilizer",
|
||||
"link_to": "Fertilizer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2021-01-01 12:13:16.055668",
|
||||
"modified": "2021-03-16 15:59:58.416154",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Home",
|
||||
|
@ -717,6 +717,18 @@ $.extend(erpnext.item, {
|
||||
.on('focus', function(e) {
|
||||
$(e.target).val('').trigger('input');
|
||||
})
|
||||
.on("awesomplete-open", () => {
|
||||
let modal = field.$input.parents('.modal-dialog')[0];
|
||||
if (modal) {
|
||||
$(modal).removeClass("modal-dialog-scrollable");
|
||||
}
|
||||
})
|
||||
.on("awesomplete-close", () => {
|
||||
let modal = field.$input.parents('.modal-dialog')[0];
|
||||
if (modal) {
|
||||
$(modal).addClass("modal-dialog-scrollable");
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -1066,7 +1066,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"max_attachments": 1,
|
||||
"modified": "2021-02-18 14:00:19.668049",
|
||||
"modified": "2021-03-15 13:41:04.108932",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
@ -1118,6 +1118,15 @@
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Manufacturing User"
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"select": 1,
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
|
@ -25,8 +25,8 @@ class PickList(Document):
|
||||
if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'):
|
||||
continue
|
||||
if not item.serial_no:
|
||||
frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format(
|
||||
frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse))),
|
||||
frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}").format(
|
||||
frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)),
|
||||
title=_("Serial Nos Required"))
|
||||
if len(item.serial_no.split('\n')) == item.picked_qty:
|
||||
continue
|
||||
@ -380,7 +380,7 @@ def create_stock_entry(pick_list):
|
||||
|
||||
stock_entry.set_incoming_rate()
|
||||
stock_entry.set_actual_qty()
|
||||
stock_entry.calculate_rate_and_amount(update_finished_item_rate=False)
|
||||
stock_entry.calculate_rate_and_amount()
|
||||
|
||||
return stock_entry.as_dict()
|
||||
|
||||
|
@ -931,12 +931,6 @@ def get_company_total_stock(item_code, company):
|
||||
WHERE `tabWarehouse`.company = '{0}' and `tabBin`.item_code = '{1}'"""
|
||||
.format(company, item_code))[0][0]
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_total_stock_value(item_code):
|
||||
query = """select sum(actual_qty) from `tabBin`, `tabItem` where
|
||||
`tabItem`.name = `tabBin`.item_code and ifnull(`tabItem`.disabled, 0) = 0 and `tabBin`.item_code = %(item_code)s"""
|
||||
return frappe.db.sql(query, debug=True)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_serial_no_details(item_code, warehouse, stock_qty, serial_no):
|
||||
args = frappe._dict({"item_code":item_code, "warehouse":warehouse, "stock_qty":stock_qty, "serial_no":serial_no})
|
||||
|
@ -198,7 +198,7 @@ def get_item_warehouse_map(filters, sle):
|
||||
else:
|
||||
qty_diff = flt(d.actual_qty)
|
||||
|
||||
value_diff = flt(d.stock_value) - flt(qty_dict.bal_val)
|
||||
value_diff = flt(d.stock_value_difference)
|
||||
|
||||
if d.posting_date < from_date:
|
||||
qty_dict.opening_qty += qty_diff
|
||||
|
@ -12,7 +12,6 @@ from datetime import timedelta
|
||||
class TestIssue(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.sql("delete from `tabService Level Agreement`")
|
||||
frappe.db.sql("delete from `tabEmployee`")
|
||||
frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
|
||||
create_service_level_agreements_for_issues()
|
||||
|
||||
|
@ -260,8 +260,7 @@ class IssueSummary(object):
|
||||
self.issue_summary_data[value]['avg_user_resolution_time'] = entry.get('avg_user_resolution_time') or 0.0
|
||||
|
||||
def get_chart_data(self):
|
||||
if not self.data:
|
||||
return None
|
||||
self.chart = []
|
||||
|
||||
labels = []
|
||||
open_issues = []
|
||||
@ -310,8 +309,7 @@ class IssueSummary(object):
|
||||
}
|
||||
|
||||
def get_report_summary(self):
|
||||
if not self.data:
|
||||
return None
|
||||
self.report_summary = []
|
||||
|
||||
open_issues = 0
|
||||
replied = 0
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="web-list-item transaction-list-item">
|
||||
<a href="/issues?name={{ doc.name }}" class="no-underline">
|
||||
<div class="row py-4 border-bottom">
|
||||
<div class="row py-4">
|
||||
<div class="col-3 d-flex align-items-center">
|
||||
{% set indicator = 'red' if doc.status == 'Open' else 'gray' %}
|
||||
{% set indicator = 'green' if doc.status == 'Closed' else indicator %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user