Merge branch 'develop' into fix-ignore-pricing-rule

This commit is contained in:
Saqib Ansari 2022-01-29 12:42:00 +05:30 committed by GitHub
commit 8a66a7ad46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
159 changed files with 1066 additions and 5133 deletions

View File

@ -28,6 +28,7 @@ ignore =
B007,
B950,
W191,
E124, # closing bracket, irritating while writing QB code
max-line-length = 200
exclude=.github/helper/semgrep_rules

View File

@ -16,6 +16,7 @@ from frappe.utils.xlsxutils import ILLEGAL_CHARACTERS_RE, handle_html
from openpyxl.styles import Font
from openpyxl.utils import get_column_letter
INVALID_VALUES = ("", None)
class BankStatementImport(DataImport):
def __init__(self, *args, **kwargs):
@ -95,6 +96,18 @@ def download_errored_template(data_import_name):
data_import = frappe.get_doc("Bank Statement Import", data_import_name)
data_import.export_errored_rows()
def parse_data_from_template(raw_data):
data = []
for i, row in enumerate(raw_data):
if all(v in INVALID_VALUES for v in row):
# empty row
continue
data.append(row)
return data
def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
"""This method runs in background job"""
@ -104,7 +117,8 @@ def start_import(data_import, bank_account, import_file_path, google_sheets_url,
file = import_file_path if import_file_path else google_sheets_url
import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records")
data = import_file.raw_data
data = parse_data_from_template(import_file.raw_data)
if import_file_path:
add_bank_account(data, bank_account)

View File

@ -2,9 +2,10 @@
# For license information, please see license.txt
from functools import reduce
import frappe
from frappe.utils import flt
from six.moves import reduce
from erpnext.controllers.status_updater import StatusUpdater

View File

@ -135,7 +135,7 @@ class OpeningInvoiceCreationTool(Document):
default_uom = frappe.db.get_single_value("Stock Settings", "stock_uom") or _("Nos")
rate = flt(row.outstanding_amount) / flt(row.qty)
return frappe._dict({
item_dict = frappe._dict({
"uom": default_uom,
"rate": rate or 0.0,
"qty": row.qty,
@ -146,6 +146,13 @@ class OpeningInvoiceCreationTool(Document):
"cost_center": cost_center
})
for dimension in get_accounting_dimensions():
item_dict.update({
dimension: row.get(dimension)
})
return item_dict
item = get_item_dict()
invoice = frappe._dict({
@ -166,7 +173,7 @@ class OpeningInvoiceCreationTool(Document):
accounting_dimension = get_accounting_dimensions()
for dimension in accounting_dimension:
invoice.update({
dimension: item.get(dimension)
dimension: self.get(dimension) or item.get(dimension)
})
return invoice

View File

@ -7,21 +7,26 @@ import frappe
from frappe.cache_manager import clear_doctype_cache
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
create_dimension,
disable_dimension,
)
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import (
get_temporary_opening_account,
)
test_dependencies = ["Customer", "Supplier"]
test_dependencies = ["Customer", "Supplier", "Accounting Dimension"]
class TestOpeningInvoiceCreationTool(unittest.TestCase):
def setUp(self):
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
make_company()
create_dimension()
def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None, invoice_number=None):
def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None, invoice_number=None, department=None):
doc = frappe.get_single("Opening Invoice Creation Tool")
args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company,
party_1=party_1, party_2=party_2, invoice_number=invoice_number)
party_1=party_1, party_2=party_2, invoice_number=invoice_number, department=department)
doc.update(args)
return doc.make_invoices()
@ -106,6 +111,19 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
doc = frappe.get_doc('Sales Invoice', inv)
doc.cancel()
def test_opening_invoice_with_accounting_dimension(self):
invoices = self.make_invoices(invoice_type="Sales", company="_Test Opening Invoice Company", department='Sales - _TOIC')
expected_value = {
"keys": ["customer", "outstanding_amount", "status", "department"],
0: ["_Test Customer", 300, "Overdue", "Sales - _TOIC"],
1: ["_Test Customer 1", 250, "Overdue", "Sales - _TOIC"],
}
self.check_expected_values(invoices, expected_value, invoice_type="Sales")
def tearDown(self):
disable_dimension()
def get_opening_invoice_creation_dict(**args):
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
company = args.get("company", "_Test Company")

View File

@ -43,6 +43,7 @@ from erpnext.setup.doctype.company.company import update_company_current_month_s
from erpnext.stock.doctype.batch.batch import set_batch_nos
from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no, get_serial_nos
from erpnext.stock.utils import calculate_mapped_packed_items_return
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -728,8 +729,11 @@ class SalesInvoice(SellingController):
def update_packing_list(self):
if cint(self.update_stock) == 1:
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self)
if cint(self.is_return) and self.return_against:
calculate_mapped_packed_items_return(self)
else:
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self)
else:
self.set('packed_items', [])

View File

@ -2192,9 +2192,9 @@ class TestSalesInvoice(unittest.TestCase):
asset.load_from_db()
expected_values = [
["2020-06-30", 1311.48, 1311.48],
["2021-06-30", 20000.0, 21311.48],
["2021-09-30", 5041.1, 26352.58]
["2020-06-30", 1366.12, 1366.12],
["2021-06-30", 20000.0, 21366.12],
["2021-09-30", 5041.1, 26407.22]
]
for i, schedule in enumerate(asset.schedules):
@ -2242,12 +2242,12 @@ class TestSalesInvoice(unittest.TestCase):
asset.load_from_db()
expected_values = [
["2020-06-30", 1311.48, 1311.48, True],
["2021-06-30", 20000.0, 21311.48, True],
["2022-06-30", 20000.0, 41311.48, False],
["2023-06-30", 20000.0, 61311.48, False],
["2024-06-30", 20000.0, 81311.48, False],
["2025-06-06", 18688.52, 100000.0, False]
["2020-06-30", 1366.12, 1366.12, True],
["2021-06-30", 20000.0, 21366.12, True],
["2022-06-30", 20000.0, 41366.12, False],
["2023-06-30", 20000.0, 61366.12, False],
["2024-06-30", 20000.0, 81366.12, False],
["2025-06-06", 18633.88, 100000.0, False]
]
for i, schedule in enumerate(asset.schedules):

View File

@ -58,7 +58,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
party = frappe.get_doc(party_type, party)
currency = party.default_currency if party.get("default_currency") else get_company_currency(company)
currency = party.get("default_currency") or currency or get_company_currency(company)
party_address, shipping_address = set_address_details(party_details, party, party_type, doctype, company, party_address, company_address, shipping_address)
set_contact_details(party_details, party, party_type)

View File

@ -5,7 +5,6 @@
import frappe
from frappe import _, scrub
from frappe.utils import cint, flt
from six import iteritems
from erpnext.accounts.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
@ -40,7 +39,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
if self.filters.show_gl_balance:
gl_balance_map = get_gl_balance(self.filters.report_date)
for party, party_dict in iteritems(self.party_total):
for party, party_dict in self.party_total.items():
if party_dict.outstanding == 0:
continue

View File

@ -17,10 +17,42 @@ from erpnext.stock.doctype.item.test_item import create_item
class TestDeferredRevenueAndExpense(unittest.TestCase):
@classmethod
def setUpClass(self):
clear_old_entries()
clear_accounts_and_items()
create_company()
self.maxDiff = None
def clear_old_entries(self):
sinv = qb.DocType("Sales Invoice")
sinv_item = qb.DocType("Sales Invoice Item")
pinv = qb.DocType("Purchase Invoice")
pinv_item = qb.DocType("Purchase Invoice Item")
# delete existing invoices with deferred items
deferred_invoices = (
qb.from_(sinv)
.join(sinv_item)
.on(sinv.name == sinv_item.parent)
.select(sinv.name)
.where(sinv_item.enable_deferred_revenue == 1)
.run()
)
if deferred_invoices:
qb.from_(sinv).delete().where(sinv.name.isin(deferred_invoices)).run()
deferred_invoices = (
qb.from_(pinv)
.join(pinv_item)
.on(pinv.name == pinv_item.parent)
.select(pinv.name)
.where(pinv_item.enable_deferred_expense == 1)
.run()
)
if deferred_invoices:
qb.from_(pinv).delete().where(pinv.name.isin(deferred_invoices)).run()
def test_deferred_revenue(self):
self.clear_old_entries()
# created deferred expense accounts, if not found
deferred_revenue_account = create_account(
account_name="Deferred Revenue",
@ -108,6 +140,8 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
self.assertEqual(report.period_total, expected)
def test_deferred_expense(self):
self.clear_old_entries()
# created deferred expense accounts, if not found
deferred_expense_account = create_account(
account_name="Deferred Expense",
@ -198,6 +232,91 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
]
self.assertEqual(report.period_total, expected)
def test_zero_months(self):
self.clear_old_entries()
# created deferred expense accounts, if not found
deferred_revenue_account = create_account(
account_name="Deferred Revenue",
parent_account="Current Liabilities - _CD",
company="_Test Company DR",
)
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
acc_settings.book_deferred_entries_based_on = "Months"
acc_settings.save()
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test Customer DR"
customer.type = "Individual"
customer.insert()
item = create_item(
"_Test Internet Subscription",
is_stock_item=0,
warehouse="All Warehouses - _CD",
company="_Test Company DR",
)
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_revenue_account
item.no_of_months = 0
item.save()
si = create_sales_invoice(
item=item.name,
company="_Test Company DR",
customer="_Test Customer DR",
debit_to="Debtors - _CD",
posting_date="2021-05-01",
parent_cost_center="Main - _CD",
cost_center="Main - _CD",
do_not_submit=True,
rate=300,
price_list_rate=300,
)
si.items[0].enable_deferred_revenue = 1
si.items[0].deferred_revenue_account = deferred_revenue_account
si.items[0].income_account = "Sales - _CD"
si.save()
si.submit()
pda = frappe.get_doc(
dict(
doctype="Process Deferred Accounting",
posting_date=nowdate(),
start_date="2021-05-01",
end_date="2021-08-01",
type="Income",
company="_Test Company DR",
)
)
pda.insert()
pda.submit()
# execute report
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),
"filter_based_on": "Date Range",
"period_start_date": "2021-05-01",
"period_end_date": "2021-08-01",
"from_fiscal_year": fiscal_year.year,
"to_fiscal_year": fiscal_year.year,
"periodicity": "Monthly",
"type": "Revenue",
"with_upcoming_postings": False,
}
)
report = Deferred_Revenue_and_Expense_Report(filters=self.filters)
report.run()
expected = [
{"key": "may_2021", "total": 300.0, "actual": 300.0},
{"key": "jun_2021", "total": 0, "actual": 0},
{"key": "jul_2021", "total": 0, "actual": 0},
{"key": "aug_2021", "total": 0, "actual": 0},
]
self.assertEqual(report.period_total, expected)
def create_company():
company = frappe.db.exists("Company", "_Test Company DR")
@ -209,15 +328,11 @@ def create_company():
company.insert()
def clear_old_entries():
def clear_accounts_and_items():
item = qb.DocType("Item")
account = qb.DocType("Account")
customer = qb.DocType("Customer")
supplier = qb.DocType("Supplier")
sinv = qb.DocType("Sales Invoice")
sinv_item = qb.DocType("Sales Invoice Item")
pinv = qb.DocType("Purchase Invoice")
pinv_item = qb.DocType("Purchase Invoice Item")
qb.from_(account).delete().where(
(account.account_name == "Deferred Revenue")
@ -228,26 +343,3 @@ def clear_old_entries():
).run()
qb.from_(customer).delete().where(customer.customer_name == "_Test Customer DR").run()
qb.from_(supplier).delete().where(supplier.supplier_name == "_Test Furniture Supplier").run()
# delete existing invoices with deferred items
deferred_invoices = (
qb.from_(sinv)
.join(sinv_item)
.on(sinv.name == sinv_item.parent)
.select(sinv.name)
.where(sinv_item.enable_deferred_revenue == 1)
.run()
)
if deferred_invoices:
qb.from_(sinv).delete().where(sinv.name.isin(deferred_invoices)).run()
deferred_invoices = (
qb.from_(pinv)
.join(pinv_item)
.on(pinv.name == pinv_item.parent)
.select(pinv.name)
.where(pinv_item.enable_deferred_expense == 1)
.run()
)
if deferred_invoices:
qb.from_(pinv).delete().where(pinv.name.isin(deferred_invoices)).run()

View File

@ -35,6 +35,7 @@
"available_for_use_date",
"column_break_23",
"gross_purchase_amount",
"asset_quantity",
"purchase_date",
"section_break_23",
"calculate_depreciation",
@ -480,6 +481,12 @@
"fieldname": "section_break_36",
"fieldtype": "Section Break",
"label": "Finance Books"
},
{
"fieldname": "asset_quantity",
"fieldtype": "Int",
"label": "Asset Quantity",
"read_only_depends_on": "eval:!doc.is_existing_asset"
}
],
"idx": 72,
@ -502,10 +509,11 @@
"link_fieldname": "asset"
}
],
"modified": "2021-06-24 14:58:51.097908",
"modified": "2022-01-18 12:57:36.741192",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
@ -542,6 +550,7 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "asset_name",
"track_changes": 1
}

View File

@ -36,6 +36,7 @@ class Asset(AccountsController):
self.validate_asset_values()
self.validate_asset_and_reference()
self.validate_item()
self.validate_cost_center()
self.set_missing_values()
self.prepare_depreciation_data()
self.validate_gross_and_purchase_amount()
@ -95,6 +96,19 @@ class Asset(AccountsController):
elif item.is_stock_item:
frappe.throw(_("Item {0} must be a non-stock item").format(self.item_code))
def validate_cost_center(self):
if not self.cost_center: return
cost_center_company = frappe.db.get_value('Cost Center', self.cost_center, 'company')
if cost_center_company != self.company:
frappe.throw(
_("Selected Cost Center {} doesn't belongs to {}").format(
frappe.bold(self.cost_center),
frappe.bold(self.company)
),
title=_("Invalid Cost Center")
)
def validate_in_use_date(self):
if not self.available_for_use_date:
frappe.throw(_("Available for use date is required"))
@ -242,8 +256,9 @@ class Asset(AccountsController):
# For first row
if has_pro_rata and not self.opening_accumulated_depreciation and n==0:
from_date = add_days(self.available_for_use_date, -1) # needed to calc depr amount for available_for_use_date too
depreciation_amount, days, months = self.get_pro_rata_amt(finance_book, depreciation_amount,
self.available_for_use_date, finance_book.depreciation_start_date)
from_date, finance_book.depreciation_start_date)
# For first depr schedule date will be the start date
# so monthly schedule date is calculated by removing month difference between use date and start date
@ -374,7 +389,9 @@ class Asset(AccountsController):
if from_date:
return from_date
return self.available_for_use_date
# since depr for available_for_use_date is not yet booked
return add_days(self.available_for_use_date, -1)
# if it returns True, depreciation_amount will not be equal for the first and last rows
def check_is_pro_rata(self, row):

View File

@ -134,6 +134,29 @@ class TestAsset(AssetSetup):
pr.cancel()
self.assertEqual(asset.docstatus, 2)
def test_purchase_of_grouped_asset(self):
create_fixed_asset_item("Rack", is_grouped_asset=1)
pr = make_purchase_receipt(item_code="Rack", qty=3, rate=100000.0, location="Test Location")
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name)
self.assertEqual(asset.asset_quantity, 3)
asset.calculate_depreciation = 1
month_end_date = get_last_day(nowdate())
purchase_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
asset.available_for_use_date = purchase_date
asset.purchase_date = purchase_date
asset.append("finance_books", {
"expected_value_after_useful_life": 10000,
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 3,
"frequency_of_depreciation": 10,
"depreciation_start_date": month_end_date
})
asset.submit()
def test_is_fixed_asset_set(self):
asset = create_asset(is_existing_asset = 1)
doc = frappe.new_doc('Purchase Invoice')
@ -207,9 +230,9 @@ class TestAsset(AssetSetup):
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
expected_gle = (
("_Test Accumulated Depreciations - _TC", 20392.16, 0.0),
("_Test Accumulated Depreciations - _TC", 20490.2, 0.0),
("_Test Fixed Asset - _TC", 0.0, 100000.0),
("_Test Gain/Loss on Asset Disposal - _TC", 54607.84, 0.0),
("_Test Gain/Loss on Asset Disposal - _TC", 54509.8, 0.0),
("Debtors - _TC", 25000.0, 0.0)
)
@ -491,10 +514,10 @@ class TestDepreciationMethods(AssetSetup):
)
expected_schedules = [
["2030-12-31", 27534.25, 27534.25],
["2031-12-31", 30000.0, 57534.25],
["2032-12-31", 30000.0, 87534.25],
["2033-01-30", 2465.75, 90000.0]
['2030-12-31', 27616.44, 27616.44],
['2031-12-31', 30000.0, 57616.44],
['2032-12-31', 30000.0, 87616.44],
['2033-01-30', 2383.56, 90000.0]
]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
@ -544,10 +567,10 @@ class TestDepreciationMethods(AssetSetup):
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
expected_schedules = [
["2030-12-31", 28493.15, 28493.15],
["2031-12-31", 35753.43, 64246.58],
["2032-12-31", 17876.71, 82123.29],
["2033-06-06", 5376.71, 87500.0]
['2030-12-31', 28630.14, 28630.14],
['2031-12-31', 35684.93, 64315.07],
['2032-12-31', 17842.47, 82157.54],
['2033-06-06', 5342.46, 87500.0]
]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
@ -580,10 +603,10 @@ class TestDepreciationMethods(AssetSetup):
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
expected_schedules = [
["2030-12-31", 11780.82, 11780.82],
["2031-12-31", 44109.59, 55890.41],
["2032-12-31", 22054.8, 77945.21],
["2033-07-12", 9554.79, 87500.0]
["2030-12-31", 11849.32, 11849.32],
["2031-12-31", 44075.34, 55924.66],
["2032-12-31", 22037.67, 77962.33],
["2033-07-12", 9537.67, 87500.0]
]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
@ -621,7 +644,7 @@ class TestDepreciationBasics(AssetSetup):
asset = create_asset(
item_code = "Macbook Pro",
calculate_depreciation = 1,
available_for_use_date = getdate("2019-12-31"),
available_for_use_date = getdate("2020-01-01"),
total_number_of_depreciations = 3,
expected_value_after_useful_life = 10000,
depreciation_start_date = getdate("2020-07-01"),
@ -632,7 +655,7 @@ class TestDepreciationBasics(AssetSetup):
["2020-07-01", 15000, 15000],
["2021-07-01", 30000, 45000],
["2022-07-01", 30000, 75000],
["2022-12-31", 15000, 90000]
["2023-01-01", 15000, 90000]
]
for i, schedule in enumerate(asset.schedules):
@ -1109,6 +1132,7 @@ class TestDepreciationBasics(AssetSetup):
self.assertEqual(gle, expected_gle)
self.assertEqual(asset.get("value_after_depreciation"), 0)
def test_expected_value_change(self):
"""
tests if changing `expected_value_after_useful_life`
@ -1130,6 +1154,15 @@ class TestDepreciationBasics(AssetSetup):
asset.reload()
self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0)
def test_asset_cost_center(self):
asset = create_asset(is_existing_asset = 1, do_not_save=1)
asset.cost_center = "Main - WP"
self.assertRaises(frappe.ValidationError, asset.submit)
asset.cost_center = "Main - _TC"
asset.submit()
def create_asset_data():
if not frappe.db.exists("Asset Category", "Computers"):
create_asset_category()
@ -1202,13 +1235,13 @@ def create_asset_category():
})
asset_category.insert()
def create_fixed_asset_item():
def create_fixed_asset_item(item_code=None, auto_create_assets=1, is_grouped_asset=0):
meta = frappe.get_meta('Asset')
naming_series = meta.get_field("naming_series").options.splitlines()[0] or 'ACC-ASS-.YYYY.-'
try:
frappe.get_doc({
item = frappe.get_doc({
"doctype": "Item",
"item_code": "Macbook Pro",
"item_code": item_code or "Macbook Pro",
"item_name": "Macbook Pro",
"description": "Macbook Pro Retina Display",
"asset_category": "Computers",
@ -1216,11 +1249,14 @@ def create_fixed_asset_item():
"stock_uom": "Nos",
"is_stock_item": 0,
"is_fixed_asset": 1,
"auto_create_assets": 1,
"auto_create_assets": auto_create_assets,
"is_grouped_asset": is_grouped_asset,
"asset_naming_series": naming_series
}).insert()
})
item.insert()
except frappe.DuplicateEntryError:
pass
return item
def set_depreciation_settings_in_company():
company = frappe.get_doc("Company", "_Test Company")

View File

@ -70,9 +70,18 @@ class BuyingController(StockController, Subcontracting):
# set contact and address details for supplier, if they are not mentioned
if getattr(self, "supplier", None):
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions,
doctype=self.doctype, company=self.company, party_address=self.supplier_address, shipping_address=self.get('shipping_address'),
fetch_payment_terms_template= not self.get('ignore_default_payment_terms_template')))
self.update_if_missing(
get_party_details(
self.supplier,
party_type="Supplier",
doctype=self.doctype,
company=self.company,
party_address=self.get("supplier_address"),
shipping_address=self.get('shipping_address'),
fetch_payment_terms_template= not self.get('ignore_default_payment_terms_template'),
ignore_permissions=self.flags.ignore_permissions
)
)
self.set_missing_item_details(for_validate)
@ -554,10 +563,13 @@ class BuyingController(StockController, Subcontracting):
# Check for asset naming series
if item_data.get('asset_naming_series'):
created_assets = []
for qty in range(cint(d.qty)):
asset = self.make_asset(d)
if item_data.get('is_grouped_asset'):
asset = self.make_asset(d, is_grouped_asset=True)
created_assets.append(asset)
else:
for qty in range(cint(d.qty)):
asset = self.make_asset(d)
created_assets.append(asset)
if len(created_assets) > 5:
# dont show asset form links if more than 5 assets are created
@ -580,14 +592,18 @@ class BuyingController(StockController, Subcontracting):
for message in messages:
frappe.msgprint(message, title="Success", indicator="green")
def make_asset(self, row):
def make_asset(self, row, is_grouped_asset=False):
if not row.asset_location:
frappe.throw(_("Row {0}: Enter location for the asset item {1}").format(row.idx, row.item_code))
item_data = frappe.db.get_value('Item',
row.item_code, ['asset_naming_series', 'asset_category'], as_dict=1)
purchase_amount = flt(row.base_rate + row.item_tax_amount)
if is_grouped_asset:
purchase_amount = flt(row.base_amount + row.item_tax_amount)
else:
purchase_amount = flt(row.base_rate + row.item_tax_amount)
asset = frappe.get_doc({
'doctype': 'Asset',
'item_code': row.item_code,
@ -601,6 +617,7 @@ class BuyingController(StockController, Subcontracting):
'calculate_depreciation': 1,
'purchase_receipt_amount': purchase_amount,
'gross_purchase_amount': purchase_amount,
'asset_quantity': row.qty if is_grouped_asset else 0,
'purchase_receipt': self.name if self.doctype == 'Purchase Receipt' else None,
'purchase_invoice': self.name if self.doctype == 'Purchase Invoice' else None
})
@ -687,7 +704,7 @@ class BuyingController(StockController, Subcontracting):
def get_asset_item_details(asset_items):
asset_items_data = {}
for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series"],
for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series", "is_grouped_asset"],
filters = {'name': ('in', asset_items)}):
asset_items_data.setdefault(d.name, d)

View File

@ -256,11 +256,7 @@ class StockController(AccountsController):
for d in self.items:
if not d.batch_no: continue
serial_nos = [sr.name for sr in frappe.get_all("Serial No",
{'batch_no': d.batch_no, 'status': 'Inactive'})]
if serial_nos:
frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None)
frappe.db.set_value("Serial No", {"batch_no": d.batch_no, "status": "Inactive"}, "batch_no", None)
d.batch_no = None
d.db_set("batch_no", None)

View File

@ -2,13 +2,14 @@
# For license information, please see license.txt
from urllib.parse import urlencode
import frappe
import requests
from frappe import _
from frappe.model.document import Document
from frappe.utils import get_url_to_form
from frappe.utils.file_manager import get_file_path
from six.moves.urllib.parse import urlencode
class LinkedInSettings(Document):

View File

@ -1,35 +0,0 @@
data = {
'desktop_icons': [
'Restaurant',
'Hotels',
'Accounts',
'Buying',
'Stock',
'HR',
'Project',
'ToDo'
],
'restricted_roles': [
'Restaurant Manager',
'Hotel Manager',
'Hotel Reservation User'
],
'custom_fields': {
'Sales Invoice': [
{
'fieldname': 'restaurant', 'fieldtype': 'Link', 'options': 'Restaurant',
'insert_after': 'customer_name', 'label': 'Restaurant',
},
{
'fieldname': 'restaurant_table', 'fieldtype': 'Link', 'options': 'Restaurant Table',
'insert_after': 'restaurant', 'label': 'Restaurant Table',
}
],
'Price List': [
{
'fieldname':'restaurant_menu', 'fieldtype':'Link', 'options':'Restaurant Menu', 'label':'Restaurant Menu',
'insert_after':'currency'
}
]
}
}

View File

@ -5,11 +5,11 @@
import csv
import math
import time
from io import StringIO
import dateutil
import frappe
from frappe import _
from six import StringIO
import erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_api as mws

View File

@ -2,13 +2,14 @@
# For license information, please see license.txt
from urllib.parse import urlencode
import frappe
import gocardless_pro
from frappe import _
from frappe.integrations.utils import create_payment_gateway, create_request_log
from frappe.model.document import Document
from frappe.utils import call_hook_method, cint, flt, get_url
from six.moves.urllib.parse import urlencode
class GoCardlessSettings(Document):

View File

@ -2,12 +2,13 @@
# For license information, please see license.txt
from urllib.parse import urlparse
import frappe
from frappe import _
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.model.document import Document
from frappe.utils.nestedset import get_root_of
from six.moves.urllib.parse import urlparse
class WoocommerceSettings(Document):

View File

@ -1,10 +1,10 @@
import base64
import hashlib
import hmac
from urllib.parse import urlparse
import frappe
from frappe import _
from six.moves.urllib.parse import urlparse
from erpnext import get_default_company

View File

@ -67,7 +67,6 @@ calendars = ["Task", "Work Order", "Leave Application", "Sales Order", "Holiday
domains = {
'Distribution': 'erpnext.domains.distribution',
'Education': 'erpnext.domains.education',
'Hospitality': 'erpnext.domains.hospitality',
'Manufacturing': 'erpnext.domains.manufacturing',
'Non Profit': 'erpnext.domains.non_profit',
'Retail': 'erpnext.domains.retail',
@ -579,13 +578,6 @@ global_search_doctypes = {
{'doctype': 'Donor Type', 'index': 10},
{'doctype': 'Membership Type', 'index': 11}
],
"Hospitality": [
{'doctype': 'Hotel Room', 'index': 0},
{'doctype': 'Hotel Room Reservation', 'index': 1},
{'doctype': 'Hotel Room Pricing', 'index': 2},
{'doctype': 'Hotel Room Package', 'index': 3},
{'doctype': 'Hotel Room Type', 'index': 4}
]
}
additional_timeline_content = {

View File

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

View File

@ -1,175 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "prompt",
"beta": 1,
"creation": "2017-12-08 12:33:56.320420",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "hotel_room_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Hotel Room Type",
"length": 0,
"no_copy": 0,
"options": "Hotel Room Type",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "capacity",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Capacity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "extra_bed_capacity",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Extra Bed Capacity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-09 12:10:50.670113",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room",
"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": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"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": "Hotel Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,13 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
class HotelRoom(Document):
def validate(self):
if not self.capacity:
self.capacity, self.extra_bed_capacity = frappe.db.get_value('Hotel Room Type',
self.hotel_room_type, ['capacity', 'extra_bed_capacity'])

View File

@ -1,23 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
test_dependencies = ["Hotel Room Package"]
test_records = [
dict(doctype="Hotel Room", name="1001",
hotel_room_type="Basic Room"),
dict(doctype="Hotel Room", name="1002",
hotel_room_type="Basic Room"),
dict(doctype="Hotel Room", name="1003",
hotel_room_type="Basic Room"),
dict(doctype="Hotel Room", name="1004",
hotel_room_type="Basic Room"),
dict(doctype="Hotel Room", name="1005",
hotel_room_type="Basic Room"),
dict(doctype="Hotel Room", name="1006",
hotel_room_type="Basic Room")
]
class TestHotelRoom(unittest.TestCase):
pass

View File

@ -1,103 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-12-08 12:35:36.572185",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "billable",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Billable",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-12-09 12:05:07.125687",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room Amenity",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class HotelRoomAmenity(Document):
pass

View File

@ -1,23 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Hotel Room Package', {
hotel_room_type: function(frm) {
if (frm.doc.hotel_room_type) {
frappe.model.with_doc('Hotel Room Type', frm.doc.hotel_room_type, () => {
let hotel_room_type = frappe.get_doc('Hotel Room Type', frm.doc.hotel_room_type);
// reset the amenities
frm.doc.amenities = [];
for (let amenity of hotel_room_type.amenities) {
let d = frm.add_child('amenities');
d.item = amenity.item;
d.billable = amenity.billable;
}
frm.refresh();
});
}
}
});

View File

@ -1,215 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "prompt",
"beta": 1,
"creation": "2017-12-08 12:43:17.211064",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "hotel_room_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Hotel Room Type",
"length": 0,
"no_copy": 0,
"options": "Hotel Room Type",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_4",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amenities",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amenities",
"length": 0,
"no_copy": 0,
"options": "Hotel Room Amenity",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-09 12:10:31.111952",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room Package",
"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": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,20 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
class HotelRoomPackage(Document):
def validate(self):
if not self.item:
item = frappe.get_doc(dict(
doctype = 'Item',
item_code = self.name,
item_group = 'Products',
is_stock_item = 0,
stock_uom = 'Unit'
))
item.insert()
self.item = item.name

View File

@ -1,47 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
test_records = [
dict(doctype='Item', item_code='Breakfast',
item_group='Products', is_stock_item=0),
dict(doctype='Item', item_code='Lunch',
item_group='Products', is_stock_item=0),
dict(doctype='Item', item_code='Dinner',
item_group='Products', is_stock_item=0),
dict(doctype='Item', item_code='WiFi',
item_group='Products', is_stock_item=0),
dict(doctype='Hotel Room Type', name="Delux Room",
capacity=4,
extra_bed_capacity=2,
amenities = [
dict(item='WiFi', billable=0)
]),
dict(doctype='Hotel Room Type', name="Basic Room",
capacity=4,
extra_bed_capacity=2,
amenities = [
dict(item='Breakfast', billable=0)
]),
dict(doctype="Hotel Room Package", name="Basic Room with Breakfast",
hotel_room_type="Basic Room",
amenities = [
dict(item="Breakfast", billable=0)
]),
dict(doctype="Hotel Room Package", name="Basic Room with Lunch",
hotel_room_type="Basic Room",
amenities = [
dict(item="Breakfast", billable=0),
dict(item="Lunch", billable=0)
]),
dict(doctype="Hotel Room Package", name="Basic Room with Dinner",
hotel_room_type="Basic Room",
amenities = [
dict(item="Breakfast", billable=0),
dict(item="Dinner", billable=0)
])
]
class TestHotelRoomPackage(unittest.TestCase):
pass

View File

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

View File

@ -1,266 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "prompt",
"beta": 1,
"creation": "2017-12-08 12:51:47.088174",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "enabled",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Enabled",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Currency",
"length": 0,
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "From Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "To Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "items",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Items",
"length": 0,
"no_copy": 0,
"options": "Hotel Room Pricing Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-09 12:10:41.559559",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room Pricing",
"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": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"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": "Hotel Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class HotelRoomPricing(Document):
pass

View File

@ -1,19 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
test_dependencies = ["Hotel Room Package"]
test_records = [
dict(doctype="Hotel Room Pricing", enabled=1,
name="Winter 2017",
from_date="2017-01-01", to_date="2017-01-10",
items = [
dict(item="Basic Room with Breakfast", rate=10000),
dict(item="Basic Room with Lunch", rate=11000),
dict(item="Basic Room with Dinner", rate=12000)
])
]
class TestHotelRoomPricing(unittest.TestCase):
pass

View File

@ -1,103 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-12-08 12:50:13.486090",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-12-09 12:04:58.641703",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room Pricing Item",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class HotelRoomPricingItem(Document):
pass

View File

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

View File

@ -1,173 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-12-08 12:50:13.486090",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "From Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "To Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "hotel_room_package",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Hotel Room Package",
"length": 0,
"no_copy": 0,
"options": "Hotel Room Package",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-11-04 03:34:02.551811",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room Pricing Package",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class HotelRoomPricingPackage(Document):
pass

View File

@ -1,8 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
class TestHotelRoomPricingPackage(unittest.TestCase):
pass

View File

@ -1,68 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Hotel Room Reservation', {
refresh: function(frm) {
if(frm.doc.docstatus == 1){
frm.add_custom_button(__('Create Invoice'), ()=> {
frm.trigger("make_invoice");
});
}
},
from_date: function(frm) {
frm.trigger("recalculate_rates");
},
to_date: function(frm) {
frm.trigger("recalculate_rates");
},
recalculate_rates: function(frm) {
if (!frm.doc.from_date || !frm.doc.to_date
|| !frm.doc.items.length){
return;
}
frappe.call({
"method": "erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation.get_room_rate",
"args": {"hotel_room_reservation": frm.doc}
}).done((r)=> {
for (var i = 0; i < r.message.items.length; i++) {
frm.doc.items[i].rate = r.message.items[i].rate;
frm.doc.items[i].amount = r.message.items[i].amount;
}
frappe.run_serially([
()=> frm.set_value("net_total", r.message.net_total),
()=> frm.refresh_field("items")
]);
});
},
make_invoice: function(frm) {
frappe.model.with_doc("Hotel Settings", "Hotel Settings", ()=>{
frappe.model.with_doctype("Sales Invoice", ()=>{
let hotel_settings = frappe.get_doc("Hotel Settings", "Hotel Settings");
let invoice = frappe.model.get_new_doc("Sales Invoice");
invoice.customer = frm.doc.customer || hotel_settings.default_customer;
if (hotel_settings.default_invoice_naming_series){
invoice.naming_series = hotel_settings.default_invoice_naming_series;
}
for (let d of frm.doc.items){
let invoice_item = frappe.model.add_child(invoice, "items")
invoice_item.item_code = d.item;
invoice_item.qty = d.qty;
invoice_item.rate = d.rate;
}
if (hotel_settings.default_taxes_and_charges){
invoice.taxes_and_charges = hotel_settings.default_taxes_and_charges;
}
frappe.set_route("Form", invoice.doctype, invoice.name);
});
});
}
});
frappe.ui.form.on('Hotel Room Reservation Item', {
item: function(frm, doctype, name) {
frm.trigger("recalculate_rates");
},
qty: function(frm) {
frm.trigger("recalculate_rates");
}
});

View File

@ -1,436 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "HTL-RES-.YYYY.-.#####",
"beta": 1,
"creation": "2017-12-08 13:01:34.829175",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "guest_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Guest Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Customer",
"length": 0,
"no_copy": 0,
"options": "Customer",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "From Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "To Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "late_checkin",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Late Checkin",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_6",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Booked\nAdvance Paid\nInvoiced\nPaid\nCompleted\nCancelled",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "items",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Items",
"length": 0,
"no_copy": 0,
"options": "Hotel Room Reservation Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "net_total",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Net Total",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Hotel Room Reservation",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 16:15:47.326951",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room Reservation",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Hotel Reservation User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,111 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_days, date_diff, flt
class HotelRoomUnavailableError(frappe.ValidationError): pass
class HotelRoomPricingNotSetError(frappe.ValidationError): pass
class HotelRoomReservation(Document):
def validate(self):
self.total_rooms = {}
self.set_rates()
self.validate_availability()
def validate_availability(self):
for i in range(date_diff(self.to_date, self.from_date)):
day = add_days(self.from_date, i)
self.rooms_booked = {}
for d in self.items:
if not d.item in self.rooms_booked:
self.rooms_booked[d.item] = 0
room_type = frappe.db.get_value("Hotel Room Package",
d.item, 'hotel_room_type')
rooms_booked = get_rooms_booked(room_type, day, exclude_reservation=self.name) \
+ d.qty + self.rooms_booked.get(d.item)
total_rooms = self.get_total_rooms(d.item)
if total_rooms < rooms_booked:
frappe.throw(_("Hotel Rooms of type {0} are unavailable on {1}").format(d.item,
frappe.format(day, dict(fieldtype="Date"))), exc=HotelRoomUnavailableError)
self.rooms_booked[d.item] += rooms_booked
def get_total_rooms(self, item):
if not item in self.total_rooms:
self.total_rooms[item] = frappe.db.sql("""
select count(*)
from
`tabHotel Room Package` package
inner join
`tabHotel Room` room on package.hotel_room_type = room.hotel_room_type
where
package.item = %s""", item)[0][0] or 0
return self.total_rooms[item]
def set_rates(self):
self.net_total = 0
for d in self.items:
net_rate = 0.0
for i in range(date_diff(self.to_date, self.from_date)):
day = add_days(self.from_date, i)
if not d.item:
continue
day_rate = frappe.db.sql("""
select
item.rate
from
`tabHotel Room Pricing Item` item,
`tabHotel Room Pricing` pricing
where
item.parent = pricing.name
and item.item = %s
and %s between pricing.from_date
and pricing.to_date""", (d.item, day))
if day_rate:
net_rate += day_rate[0][0]
else:
frappe.throw(
_("Please set Hotel Room Rate on {}").format(
frappe.format(day, dict(fieldtype="Date"))), exc=HotelRoomPricingNotSetError)
d.rate = net_rate
d.amount = net_rate * flt(d.qty)
self.net_total += d.amount
@frappe.whitelist()
def get_room_rate(hotel_room_reservation):
"""Calculate rate for each day as it may belong to different Hotel Room Pricing Item"""
doc = frappe.get_doc(json.loads(hotel_room_reservation))
doc.set_rates()
return doc.as_dict()
def get_rooms_booked(room_type, day, exclude_reservation=None):
exclude_condition = ''
if exclude_reservation:
exclude_condition = 'and reservation.name != {0}'.format(frappe.db.escape(exclude_reservation))
return frappe.db.sql("""
select sum(item.qty)
from
`tabHotel Room Package` room_package,
`tabHotel Room Reservation Item` item,
`tabHotel Room Reservation` reservation
where
item.parent = reservation.name
and room_package.item = item.item
and room_package.hotel_room_type = %s
and reservation.docstatus = 1
{exclude_condition}
and %s between reservation.from_date
and reservation.to_date""".format(exclude_condition=exclude_condition),
(room_type, day))[0][0] or 0

View File

@ -1,9 +0,0 @@
frappe.views.calendar["Hotel Room Reservation"] = {
field_map: {
"start": "from_date",
"end": "to_date",
"id": "name",
"title": "guest_name",
"status": "status"
}
}

View File

@ -1,65 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
import frappe
from erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation import (
HotelRoomPricingNotSetError,
HotelRoomUnavailableError,
)
test_dependencies = ["Hotel Room Package", "Hotel Room Pricing", "Hotel Room"]
class TestHotelRoomReservation(unittest.TestCase):
def setUp(self):
frappe.db.sql("delete from `tabHotel Room Reservation`")
frappe.db.sql("delete from `tabHotel Room Reservation Item`")
def test_reservation(self):
reservation = make_reservation(
from_date="2017-01-01",
to_date="2017-01-03",
items=[
dict(item="Basic Room with Dinner", qty=2)
]
)
reservation.insert()
self.assertEqual(reservation.net_total, 48000)
def test_price_not_set(self):
reservation = make_reservation(
from_date="2016-01-01",
to_date="2016-01-03",
items=[
dict(item="Basic Room with Dinner", qty=2)
]
)
self.assertRaises(HotelRoomPricingNotSetError, reservation.insert)
def test_room_unavailable(self):
reservation = make_reservation(
from_date="2017-01-01",
to_date="2017-01-03",
items=[
dict(item="Basic Room with Dinner", qty=2),
]
)
reservation.insert()
reservation = make_reservation(
from_date="2017-01-01",
to_date="2017-01-03",
items=[
dict(item="Basic Room with Dinner", qty=20),
]
)
self.assertRaises(HotelRoomUnavailableError, reservation.insert)
def make_reservation(**kwargs):
kwargs["doctype"] = "Hotel Room Reservation"
if not "guest_name" in kwargs:
kwargs["guest_name"] = "Test Guest"
doc = frappe.get_doc(kwargs)
return doc

View File

@ -1,195 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2017-12-08 12:58:21.733330",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "qty",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Qty",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Currency",
"length": 0,
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-12-09 12:04:34.562956",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room Reservation Item",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class HotelRoomReservationItem(Document):
pass

View File

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

View File

@ -1,204 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "prompt",
"beta": 1,
"creation": "2017-12-08 12:38:29.485175",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "capacity",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Capacity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "extra_bed_capacity",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Extra Bed Capacity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_3",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amenities",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amenities",
"length": 0,
"no_copy": 0,
"options": "Hotel Room Amenity",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-09 12:10:23.355486",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room Type",
"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": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"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": "Hotel Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class HotelRoomType(Document):
pass

View File

@ -1,8 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
class TestHotelRoomType(unittest.TestCase):
pass

View File

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

View File

@ -1,175 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2017-12-08 17:50:24.523107",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_customer",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Default Customer",
"length": 0,
"no_copy": 0,
"options": "Customer",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_taxes_and_charges",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Taxes and Charges",
"length": 0,
"no_copy": 0,
"options": "Sales Taxes and Charges Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_invoice_naming_series",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Invoice Naming Series",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-09 12:11:12.857308",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "Hotel Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class HotelSettings(Document):
pass

View File

@ -1,8 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
class TestHotelSettings(unittest.TestCase):
pass

View File

@ -1,22 +0,0 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Hotel Room Occupancy"] = {
"filters": [
{
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.datetime.now_date(),
"reqd":1
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.now_date(),
"reqd":1
}
]
}

View File

@ -1,26 +0,0 @@
{
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2017-12-09 14:31:26.306705",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2017-12-09 14:31:26.306705",
"modified_by": "Administrator",
"module": "Hotels",
"name": "Hotel Room Occupancy",
"owner": "Administrator",
"ref_doctype": "Hotel Room Reservation",
"report_name": "Hotel Room Occupancy",
"report_type": "Script Report",
"roles": [
{
"role": "System Manager"
},
{
"role": "Hotel Reservation User"
}
]
}

View File

@ -1,34 +0,0 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.utils import add_days, date_diff
from erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation import get_rooms_booked
def execute(filters=None):
columns = get_columns(filters)
data = get_data(filters)
return columns, data
def get_columns(filters):
columns = [
dict(label=_("Room Type"), fieldname="room_type"),
dict(label=_("Rooms Booked"), fieldtype="Int")
]
return columns
def get_data(filters):
out = []
for room_type in frappe.get_all('Hotel Room Type'):
total_booked = 0
for i in range(date_diff(filters.to_date, filters.from_date)):
day = add_days(filters.from_date, i)
total_booked += get_rooms_booked(room_type.name, day)
out.append([room_type.name, total_booked])
return out

View File

@ -22,6 +22,7 @@ from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
from erpnext.hr.utils import (
get_holiday_dates_for_employee,
get_leave_period,
set_employee_name,
share_doc_with_approver,
@ -159,33 +160,57 @@ class LeaveApplication(Document):
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name))
def update_attendance(self):
if self.status == "Approved":
for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
date = dt.strftime("%Y-%m-%d")
status = "Half Day" if self.half_day_date and getdate(date) == getdate(self.half_day_date) else "On Leave"
attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee,
attendance_date = date, docstatus = ('!=', 2)))
if self.status != "Approved":
return
holiday_dates = []
if not frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"):
holiday_dates = get_holiday_dates_for_employee(self.employee, self.from_date, self.to_date)
for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
date = dt.strftime("%Y-%m-%d")
attendance_name = frappe.db.exists("Attendance", dict(employee = self.employee,
attendance_date = date, docstatus = ('!=', 2)))
# don't mark attendance for holidays
# if leave type does not include holidays within leaves as leaves
if date in holiday_dates:
if attendance_name:
# update existing attendance, change absent to on leave
doc = frappe.get_doc('Attendance', attendance_name)
if doc.status != status:
doc.db_set('status', status)
doc.db_set('leave_type', self.leave_type)
doc.db_set('leave_application', self.name)
else:
# make new attendance and submit it
doc = frappe.new_doc("Attendance")
doc.employee = self.employee
doc.employee_name = self.employee_name
doc.attendance_date = date
doc.company = self.company
doc.leave_type = self.leave_type
doc.leave_application = self.name
doc.status = status
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True)
doc.submit()
# cancel and delete existing attendance for holidays
attendance = frappe.get_doc("Attendance", attendance_name)
attendance.flags.ignore_permissions = True
if attendance.docstatus == 1:
attendance.cancel()
frappe.delete_doc("Attendance", attendance_name, force=1)
continue
self.create_or_update_attendance(attendance_name, date)
def create_or_update_attendance(self, attendance_name, date):
status = "Half Day" if self.half_day_date and getdate(date) == getdate(self.half_day_date) else "On Leave"
if attendance_name:
# update existing attendance, change absent to on leave
doc = frappe.get_doc('Attendance', attendance_name)
if doc.status != status:
doc.db_set({
'status': status,
'leave_type': self.leave_type,
'leave_application': self.name
})
else:
# make new attendance and submit it
doc = frappe.new_doc("Attendance")
doc.employee = self.employee
doc.employee_name = self.employee_name
doc.attendance_date = date
doc.company = self.company
doc.leave_type = self.leave_type
doc.leave_application = self.name
doc.status = status
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True)
doc.submit()
def cancel_attendance(self):
if self.docstatus == 2:

View File

@ -5,7 +5,16 @@ import unittest
import frappe
from frappe.permissions import clear_user_permissions_for_doctype
from frappe.utils import add_days, add_months, getdate, nowdate
from frappe.utils import (
add_days,
add_months,
get_first_day,
get_last_day,
get_year_ending,
get_year_start,
getdate,
nowdate,
)
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
@ -19,6 +28,10 @@ from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import (
create_assignment_for_multiple_employees,
)
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
make_holiday_list,
make_leave_application,
)
test_dependencies = ["Leave Allocation", "Leave Block List", "Employee"]
@ -61,13 +74,15 @@ class TestLeaveApplication(unittest.TestCase):
for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Leave Ledger Entry"]:
frappe.db.sql("DELETE FROM `tab%s`" % dt) #nosec
frappe.set_user("Administrator")
@classmethod
def setUpClass(cls):
set_leave_approver()
frappe.db.sql("delete from tabAttendance where employee='_T-Employee-00001'")
def tearDown(self):
frappe.set_user("Administrator")
frappe.db.rollback()
def _clear_roles(self):
frappe.db.sql("""delete from `tabHas Role` where parent in
@ -106,6 +121,72 @@ class TestLeaveApplication(unittest.TestCase):
for d in ('2018-01-01', '2018-01-02', '2018-01-03'):
self.assertTrue(getdate(d) in dates)
def test_attendance_for_include_holidays(self):
# Case 1: leave type with 'Include holidays within leaves as leaves' enabled
frappe.delete_doc_if_exists("Leave Type", "Test Include Holidays", force=1)
leave_type = frappe.get_doc(dict(
leave_type_name="Test Include Holidays",
doctype="Leave Type",
include_holiday=True
)).insert()
date = getdate()
make_allocation_record(leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date))
holiday_list = make_holiday_list()
frappe.db.set_value("Company", "_Test Company", "default_holiday_list", holiday_list)
first_sunday = get_first_sunday(holiday_list)
leave_application = make_leave_application("_T-Employee-00001", first_sunday, add_days(first_sunday, 3), leave_type.name)
leave_application.reload()
self.assertEqual(leave_application.total_leave_days, 4)
self.assertEqual(frappe.db.count('Attendance', {'leave_application': leave_application.name}), 4)
leave_application.cancel()
def test_attendance_update_for_exclude_holidays(self):
# Case 2: leave type with 'Include holidays within leaves as leaves' disabled
frappe.delete_doc_if_exists("Leave Type", "Test Do Not Include Holidays", force=1)
leave_type = frappe.get_doc(dict(
leave_type_name="Test Do Not Include Holidays",
doctype="Leave Type",
include_holiday=False
)).insert()
date = getdate()
make_allocation_record(leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date))
holiday_list = make_holiday_list()
frappe.db.set_value("Company", "_Test Company", "default_holiday_list", holiday_list)
first_sunday = get_first_sunday(holiday_list)
# already marked attendance on a holiday should be deleted in this case
config = {
"doctype": "Attendance",
"employee": "_T-Employee-00001",
"status": "Present"
}
attendance_on_holiday = frappe.get_doc(config)
attendance_on_holiday.attendance_date = first_sunday
attendance_on_holiday.save()
# already marked attendance on a non-holiday should be updated
attendance = frappe.get_doc(config)
attendance.attendance_date = add_days(first_sunday, 3)
attendance.save()
leave_application = make_leave_application("_T-Employee-00001", first_sunday, add_days(first_sunday, 3), leave_type.name)
leave_application.reload()
# holiday should be excluded while marking attendance
self.assertEqual(leave_application.total_leave_days, 3)
self.assertEqual(frappe.db.count("Attendance", {"leave_application": leave_application.name}), 3)
# attendance on holiday deleted
self.assertFalse(frappe.db.exists("Attendance", attendance_on_holiday.name))
# attendance on non-holiday updated
self.assertEqual(frappe.db.get_value("Attendance", attendance.name, "status"), "On Leave")
def test_block_list(self):
self._clear_roles()
@ -241,7 +322,13 @@ class TestLeaveApplication(unittest.TestCase):
leave_period = get_leave_period()
today = nowdate()
holiday_list = 'Test Holiday List for Optional Holiday'
optional_leave_date = add_days(today, 7)
employee = get_employee()
default_holiday_list = make_holiday_list()
frappe.db.set_value("Company", "_Test Company", "default_holiday_list", default_holiday_list)
first_sunday = get_first_sunday(default_holiday_list)
optional_leave_date = add_days(first_sunday, 1)
if not frappe.db.exists('Holiday List', holiday_list):
frappe.get_doc(dict(
@ -253,7 +340,6 @@ class TestLeaveApplication(unittest.TestCase):
dict(holiday_date = optional_leave_date, description = 'Test')
]
)).insert()
employee = get_employee()
frappe.db.set_value('Leave Period', leave_period.name, 'optional_holiday_list', holiday_list)
leave_type = 'Test Optional Type'
@ -266,7 +352,7 @@ class TestLeaveApplication(unittest.TestCase):
allocate_leaves(employee, leave_period, leave_type, 10)
date = add_days(today, 6)
date = add_days(first_sunday, 2)
leave_application = frappe.get_doc(dict(
doctype = 'Leave Application',
@ -637,13 +723,13 @@ def create_carry_forwarded_allocation(employee, leave_type):
carry_forward=1)
leave_allocation.submit()
def make_allocation_record(employee=None, leave_type=None):
def make_allocation_record(employee=None, leave_type=None, from_date=None, to_date=None):
allocation = frappe.get_doc({
"doctype": "Leave Allocation",
"employee": employee or "_T-Employee-00001",
"leave_type": leave_type or "_Test Leave Type",
"from_date": "2013-01-01",
"to_date": "2019-12-31",
"from_date": from_date or "2013-01-01",
"to_date": to_date or "2019-12-31",
"new_leaves_allocated": 30
})
@ -692,3 +778,16 @@ def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, el
}).insert()
allocate_leave.submit()
def get_first_sunday(holiday_list):
month_start_date = get_first_day(nowdate())
month_end_date = get_last_day(nowdate())
first_sunday = frappe.db.sql("""
select holiday_date from `tabHoliday`
where parent = %s
and holiday_date between %s and %s
order by holiday_date
""", (holiday_list, month_start_date, month_end_date))[0][0]
return first_sunday

View File

@ -4,6 +4,7 @@
import unittest
import frappe
from frappe.utils import format_date
from frappe.utils.data import add_days, formatdate, today
from erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule import (
@ -82,6 +83,13 @@ class TestMaintenanceSchedule(unittest.TestCase):
#checks if visit status is back updated in schedule
self.assertTrue(ms.schedules[1].completion_status, "Partially Completed")
self.assertEqual(format_date(visit.mntc_date), format_date(ms.schedules[1].actual_date))
#checks if visit status is updated on cancel
visit.cancel()
ms.reload()
self.assertTrue(ms.schedules[1].completion_status, "Pending")
self.assertEqual(ms.schedules[1].actual_date, None)
def test_serial_no_filters(self):
# Without serial no. set in schedule -> returns None

View File

@ -4,7 +4,7 @@
import frappe
from frappe import _
from frappe.utils import get_datetime
from frappe.utils import format_date, get_datetime
from erpnext.utilities.transaction_base import TransactionBase
@ -28,20 +28,24 @@ class MaintenanceVisit(TransactionBase):
if item_ref:
start_date, end_date = frappe.db.get_value('Maintenance Schedule Item', item_ref, ['start_date', 'end_date'])
if get_datetime(self.mntc_date) < get_datetime(start_date) or get_datetime(self.mntc_date) > get_datetime(end_date):
frappe.throw(_("Date must be between {0} and {1}").format(start_date, end_date))
frappe.throw(_("Date must be between {0} and {1}")
.format(format_date(start_date), format_date(end_date)))
def validate(self):
self.validate_serial_no()
self.validate_maintenance_date()
self.validate_purpose_table()
def update_completion_status(self):
def update_status_and_actual_date(self, cancel=False):
status = "Pending"
actual_date = None
if not cancel:
status = self.completion_status
actual_date = self.mntc_date
if self.maintenance_schedule_detail:
frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'completion_status', self.completion_status)
def update_actual_date(self):
if self.maintenance_schedule_detail:
frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'actual_date', self.mntc_date)
frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'completion_status', status)
frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'actual_date', actual_date)
def update_customer_issue(self, flag):
if not self.maintenance_schedule:
@ -102,12 +106,12 @@ class MaintenanceVisit(TransactionBase):
def on_submit(self):
self.update_customer_issue(1)
frappe.db.set(self, 'status', 'Submitted')
self.update_completion_status()
self.update_actual_date()
self.update_status_and_actual_date()
def on_cancel(self):
self.check_if_last_visit()
frappe.db.set(self, 'status', 'Cancelled')
self.update_status_and_actual_date(cancel=True)
def on_update(self):
pass

View File

@ -331,7 +331,7 @@ frappe.ui.form.on("BOM", {
});
});
if (has_template_rm) {
if (has_template_rm && has_template_rm.length) {
dialog.fields_dict.items.grid.refresh();
}
},
@ -467,7 +467,8 @@ var get_bom_material_detail = function(doc, cdt, cdn, scrap_items) {
"uom": d.uom,
"stock_uom": d.stock_uom,
"conversion_factor": d.conversion_factor,
"sourced_by_supplier": d.sourced_by_supplier
"sourced_by_supplier": d.sourced_by_supplier,
"do_not_explode": d.do_not_explode
},
callback: function(r) {
d = locals[cdt][cdn];
@ -640,6 +641,13 @@ frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) {
});
});
frappe.ui.form.on("BOM Item", {
do_not_explode: function(frm, cdt, cdn) {
get_bom_material_detail(frm.doc, cdt, cdn, false);
}
})
frappe.ui.form.on("BOM Item", "qty", function(frm, cdt, cdn) {
var d = locals[cdt][cdn];
d.stock_qty = d.qty * d.conversion_factor;

View File

@ -149,6 +149,7 @@ class BOM(WebsiteGenerator):
self.set_bom_material_details()
self.set_bom_scrap_items_detail()
self.validate_materials()
self.validate_transfer_against()
self.set_routing_operations()
self.validate_operations()
self.calculate_cost()
@ -203,6 +204,10 @@ class BOM(WebsiteGenerator):
for item in self.get("items"):
self.validate_bom_currency(item)
item.bom_no = ''
if not item.do_not_explode:
item.bom_no = item.bom_no
ret = self.get_bom_material_detail({
"company": self.company,
"item_code": item.item_code,
@ -214,8 +219,10 @@ class BOM(WebsiteGenerator):
"uom": item.uom,
"stock_uom": item.stock_uom,
"conversion_factor": item.conversion_factor,
"sourced_by_supplier": item.sourced_by_supplier
"sourced_by_supplier": item.sourced_by_supplier,
"do_not_explode": item.do_not_explode
})
for r in ret:
if not item.get(r):
item.set(r, ret[r])
@ -267,6 +274,9 @@ class BOM(WebsiteGenerator):
'sourced_by_supplier' : args.get('sourced_by_supplier', 0)
}
if args.get('do_not_explode'):
ret_item['bom_no'] = ''
return ret_item
def validate_bom_currency(self, item):
@ -681,6 +691,12 @@ class BOM(WebsiteGenerator):
if act_pbom and act_pbom[0][0]:
frappe.throw(_("Cannot deactivate or cancel BOM as it is linked with other BOMs"))
def validate_transfer_against(self):
if not self.with_operations:
self.transfer_material_against = "Work Order"
if not self.transfer_material_against and not self.is_new():
frappe.throw(_("Setting {} is required").format(self.meta.get_label("transfer_material_against")), title=_("Missing value"))
def set_routing_operations(self):
if self.routing and self.with_operations and not self.operations:
self.get_routing()

View File

@ -385,6 +385,53 @@ class TestBOM(ERPNextTestCase):
self.assertNotEqual(len(test_items), len(filtered), msg="Item filtering showing excessive results")
self.assertTrue(0 < len(filtered) <= 3, msg="Item filtering showing excessive results")
def test_exclude_exploded_items_from_bom(self):
bom_no = get_default_bom()
new_bom = frappe.copy_doc(frappe.get_doc('BOM', bom_no))
for row in new_bom.items:
if row.item_code == '_Test Item Home Desktop Manufactured':
self.assertTrue(row.bom_no)
row.do_not_explode = True
new_bom.docstatus = 0
new_bom.save()
new_bom.load_from_db()
for row in new_bom.items:
if row.item_code == '_Test Item Home Desktop Manufactured' and row.do_not_explode:
self.assertFalse(row.bom_no)
new_bom.delete()
def test_valid_transfer_defaults(self):
bom_with_op = frappe.db.get_value("BOM", {"item": "_Test FG Item 2", "with_operations": 1, "is_active": 1})
bom = frappe.copy_doc(frappe.get_doc("BOM", bom_with_op), ignore_no_copy=False)
# test defaults
bom.docstatus = 0
bom.transfer_material_against = None
bom.insert()
self.assertEqual(bom.transfer_material_against, "Work Order")
bom.reload()
bom.transfer_material_against = None
with self.assertRaises(frappe.ValidationError):
bom.save()
bom.reload()
# test saner default
bom.transfer_material_against = "Job Card"
bom.with_operations = 0
bom.save()
self.assertEqual(bom.transfer_material_against, "Work Order")
# test no value on existing doc
bom.transfer_material_against = None
bom.with_operations = 0
bom.save()
self.assertEqual(bom.transfer_material_against, "Work Order")
bom.delete()
def get_default_bom(item_code="_Test FG Item 2"):
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})

View File

@ -10,6 +10,7 @@
"item_name",
"operation",
"column_break_3",
"do_not_explode",
"bom_no",
"source_warehouse",
"allow_alternative_item",
@ -73,6 +74,7 @@
"fieldtype": "Column Break"
},
{
"depends_on": "eval:!doc.do_not_explode",
"fieldname": "bom_no",
"fieldtype": "Link",
"in_filter": 1,
@ -284,18 +286,25 @@
"fieldname": "sourced_by_supplier",
"fieldtype": "Check",
"label": "Sourced by Supplier"
},
{
"default": "0",
"fieldname": "do_not_explode",
"fieldtype": "Check",
"label": "Do Not Explode"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-10-08 14:19:37.563300",
"modified": "2022-01-24 16:57:57.020232",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Item",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"states": []
}

View File

@ -948,6 +948,7 @@ def get_materials_from_other_locations(item, warehouses, new_mr_items, company):
warehouses, item.get("quantity"), company, ignore_validation=True)
required_qty = item.get("quantity")
# get available material by transferring to production warehouse
for d in locations:
if required_qty <=0: return
@ -958,12 +959,14 @@ def get_materials_from_other_locations(item, warehouses, new_mr_items, company):
new_dict.update({
"quantity": quantity,
"material_request_type": "Material Transfer",
"uom": new_dict.get("stock_uom"), # internal transfer should be in stock UOM
"from_warehouse": d.get("warehouse")
})
required_qty -= quantity
new_mr_items.append(new_dict)
# raise purchase request for remaining qty
if required_qty:
stock_uom, purchase_uom = frappe.db.get_value(
'Item',

View File

@ -911,6 +911,54 @@ class TestWorkOrder(ERPNextTestCase):
self.assertEqual(wo1.operations[0].time_in_mins, wo2.operations[0].time_in_mins)
def test_partial_manufacture_entries(self):
cancel_stock_entry = []
frappe.db.set_value("Manufacturing Settings", None,
"backflush_raw_materials_based_on", "Material Transferred for Manufacture")
wo_order = make_wo_order_test_record(planned_start_date=now(), qty=100)
ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item",
target="_Test Warehouse - _TC", qty=120, basic_rate=5000.0)
ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
target="_Test Warehouse - _TC", qty=240, basic_rate=1000.0)
cancel_stock_entry.extend([ste1.name, ste2.name])
sm = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 100))
for row in sm.get('items'):
if row.get('item_code') == '_Test Item':
row.qty = 110
sm.submit()
cancel_stock_entry.append(sm.name)
s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 90))
for row in s.get('items'):
if row.get('item_code') == '_Test Item':
self.assertEqual(row.get('qty'), 100)
s.submit()
cancel_stock_entry.append(s.name)
s1 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 5))
for row in s1.get('items'):
if row.get('item_code') == '_Test Item':
self.assertEqual(row.get('qty'), 5)
s1.submit()
cancel_stock_entry.append(s1.name)
s2 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 5))
for row in s2.get('items'):
if row.get('item_code') == '_Test Item':
self.assertEqual(row.get('qty'), 5)
cancel_stock_entry.reverse()
for ste in cancel_stock_entry:
doc = frappe.get_doc("Stock Entry", ste)
doc.cancel()
frappe.db.set_value("Manufacturing Settings", None,
"backflush_raw_materials_based_on", "BOM")
def update_job_card(job_card, jc_qty=None):
employee = frappe.db.get_value('Employee', {'status': 'Active'}, 'name')

View File

@ -333,12 +333,13 @@
"options": "fa fa-wrench"
},
{
"default": "Work Order",
"depends_on": "operations",
"fetch_from": "bom_no.transfer_material_against",
"fetch_if_empty": 1,
"fieldname": "transfer_material_against",
"fieldtype": "Select",
"label": "Transfer Material Against",
"options": "Work Order\nJob Card"
"options": "\nWork Order\nJob Card"
},
{
"fieldname": "operations",
@ -574,7 +575,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
"modified": "2021-11-08 17:36:07.016300",
"modified": "2022-01-24 21:18:12.160114",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",
@ -607,6 +608,7 @@
],
"sort_field": "modified",
"sort_order": "ASC",
"states": [],
"title_field": "production_item",
"track_changes": 1,
"track_seen": 1

View File

@ -65,6 +65,7 @@ class WorkOrder(Document):
self.validate_warehouse_belongs_to_company()
self.calculate_operating_cost()
self.validate_qty()
self.validate_transfer_against()
self.validate_operation_time()
self.status = self.get_status()
@ -72,6 +73,7 @@ class WorkOrder(Document):
self.set_required_items(reset_only_qty = len(self.get("required_items")))
def validate_sales_order(self):
if self.sales_order:
self.check_sales_order_on_hold_or_close()
@ -625,6 +627,16 @@ class WorkOrder(Document):
if not self.qty > 0:
frappe.throw(_("Quantity to Manufacture must be greater than 0."))
def validate_transfer_against(self):
if not self.docstatus == 1:
# let user configure operations until they're ready to submit
return
if not self.operations:
self.transfer_material_against = "Work Order"
if not self.transfer_material_against:
frappe.throw(_("Setting {} is required").format(self.meta.get_label("transfer_material_against")), title=_("Missing value"))
def validate_operation_time(self):
for d in self.operations:
if not d.time_in_mins > 0:

View File

@ -4,6 +4,39 @@
frappe.query_reports["BOM Operations Time"] = {
"filters": [
{
"fieldname": "item_code",
"label": __("Item Code"),
"fieldtype": "Link",
"width": "100",
"options": "Item",
"get_query": () =>{
return {
filters: { "disabled": 0, "is_stock_item": 1 }
}
}
},
{
"fieldname": "bom_id",
"label": __("BOM ID"),
"fieldtype": "MultiSelectList",
"width": "100",
"options": "BOM",
"get_data": function(txt) {
return frappe.db.get_link_options("BOM", txt);
},
"get_query": () =>{
return {
filters: { "docstatus": 1, "is_active": 1, "with_operations": 1 }
}
}
},
{
"fieldname": "workstation",
"label": __("Workstation"),
"fieldtype": "Link",
"width": "100",
"options": "Workstation"
},
]
};

View File

@ -1,14 +1,16 @@
{
"add_total_row": 0,
"add_total_row": 1,
"columns": [],
"creation": "2020-03-03 01:41:20.862521",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"letter_head": "",
"modified": "2020-03-03 01:41:20.862521",
"modified": "2022-01-20 14:21:47.771591",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Operations Time",

View File

@ -12,19 +12,15 @@ def execute(filters=None):
return columns, data
def get_data(filters):
data = []
bom_wise_data = {}
bom_data, report_data = [], []
bom_data = []
for d in frappe.db.sql("""
SELECT
bom.name, bom.item, bom.item_name, bom.uom,
bomps.operation, bomps.workstation, bomps.time_in_mins
FROM `tabBOM` bom, `tabBOM Operation` bomps
WHERE
bom.docstatus = 1 and bom.is_active = 1 and bom.name = bomps.parent
""", as_dict=1):
bom_operation_data = get_filtered_data(filters)
for d in bom_operation_data:
row = get_args()
if d.name not in bom_data:
bom_wise_data[d.name] = []
bom_data.append(d.name)
row.update(d)
else:
@ -34,14 +30,49 @@ def get_data(filters):
"time_in_mins": d.time_in_mins
})
data.append(row)
# maintain BOM wise data for grouping such as:
# {"BOM A": [{Row1}, {Row2}], "BOM B": ...}
bom_wise_data[d.name].append(row)
used_as_subassembly_items = get_bom_count(bom_data)
for d in data:
d.used_as_subassembly_items = used_as_subassembly_items.get(d.name, 0)
for d in bom_wise_data:
for row in bom_wise_data[d]:
row.used_as_subassembly_items = used_as_subassembly_items.get(row.name, 0)
report_data.append(row)
return data
return report_data
def get_filtered_data(filters):
bom = frappe.qb.DocType("BOM")
bom_ops = frappe.qb.DocType("BOM Operation")
bom_ops_query = (
frappe.qb.from_(bom)
.join(bom_ops).on(bom.name == bom_ops.parent)
.select(
bom.name, bom.item, bom.item_name, bom.uom,
bom_ops.operation, bom_ops.workstation, bom_ops.time_in_mins
).where(
(bom.docstatus == 1)
& (bom.is_active == 1)
)
)
if filters.get("item_code"):
bom_ops_query = bom_ops_query.where(bom.item == filters.get("item_code"))
if filters.get("bom_id"):
bom_ops_query = bom_ops_query.where(bom.name.isin(filters.get("bom_id")))
if filters.get("workstation"):
bom_ops_query = bom_ops_query.where(
bom_ops.workstation == filters.get("workstation")
)
bom_operation_data = bom_ops_query.run(as_dict=True)
return bom_operation_data
def get_bom_count(bom_data):
data = frappe.get_all("BOM Item",
@ -68,13 +99,13 @@ def get_columns(filters):
"options": "BOM",
"fieldname": "name",
"fieldtype": "Link",
"width": 140
"width": 220
}, {
"label": _("BOM Item Code"),
"label": _("Item Code"),
"options": "Item",
"fieldname": "item",
"fieldtype": "Link",
"width": 140
"width": 150
}, {
"label": _("Item Name"),
"fieldname": "item_name",
@ -85,13 +116,13 @@ def get_columns(filters):
"options": "UOM",
"fieldname": "uom",
"fieldtype": "Link",
"width": 140
"width": 100
}, {
"label": _("Operation"),
"options": "Operation",
"fieldname": "operation",
"fieldtype": "Link",
"width": 120
"width": 140
}, {
"label": _("Workstation"),
"options": "Workstation",
@ -101,11 +132,11 @@ def get_columns(filters):
}, {
"label": _("Time (In Mins)"),
"fieldname": "time_in_mins",
"fieldtype": "Int",
"width": 140
"fieldtype": "Float",
"width": 120
}, {
"label": _("Sub-assembly BOM Count"),
"fieldname": "used_as_subassembly_items",
"fieldtype": "Int",
"width": 180
"width": 200
}]

View File

@ -15,10 +15,8 @@ Portal
Maintenance
Education
Regional
Restaurant
ERPNext Integrations
Non Profit
Hotels
Quality Management
Communication
Loan Management

View File

@ -1,3 +1,4 @@
[pre_model_sync]
erpnext.patches.v12_0.update_is_cancelled_field
erpnext.patches.v11_0.rename_production_order_to_work_order
erpnext.patches.v11_0.refactor_naming_series
@ -226,7 +227,6 @@ erpnext.patches.v13_0.updates_for_multi_currency_payroll
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
execute:frappe.delete_doc("Report", "Quoted Item Comparison")
erpnext.patches.v13_0.update_member_email_address
erpnext.patches.v13_0.updates_for_multi_currency_payroll
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
erpnext.patches.v13_0.update_pos_closing_entry_in_merge_log
erpnext.patches.v13_0.add_po_to_global_search
@ -252,7 +252,7 @@ erpnext.patches.v12_0.add_gst_category_in_delivery_note
erpnext.patches.v12_0.purchase_receipt_status
erpnext.patches.v13_0.fix_non_unique_represents_company
erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing
erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021
erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021 #17-01-2022
erpnext.patches.v13_0.update_shipment_status
erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting
erpnext.patches.v13_0.germany_make_custom_fields
@ -266,7 +266,6 @@ erpnext.patches.v13_0.set_training_event_attendance
erpnext.patches.v13_0.bill_for_rejected_quantity_in_purchase_invoice
erpnext.patches.v13_0.rename_issue_status_hold_to_on_hold
erpnext.patches.v13_0.update_response_by_variance
erpnext.patches.v13_0.bill_for_rejected_quantity_in_purchase_invoice
erpnext.patches.v13_0.update_job_card_details
erpnext.patches.v13_0.update_level_in_bom #1234sswef
erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry
@ -285,11 +284,9 @@ erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries
erpnext.patches.v13_0.einvoicing_deprecation_warning
execute:frappe.reload_doc("erpnext_integrations", "doctype", "TaxJar Settings")
execute:frappe.reload_doc("erpnext_integrations", "doctype", "Product Tax Category")
erpnext.patches.v14_0.delete_einvoicing_doctypes
erpnext.patches.v13_0.custom_fields_for_taxjar_integration #08-11-2021
erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
erpnext.patches.v13_0.create_gst_payment_entry_fields #27-11-2021
erpnext.patches.v14_0.delete_shopify_doctypes
erpnext.patches.v13_0.fix_invoice_statuses
erpnext.patches.v13_0.replace_supplier_item_group_with_party_specific_item
erpnext.patches.v13_0.update_dates_in_tax_withholding_category
@ -312,19 +309,29 @@ erpnext.patches.v13_0.item_naming_series_not_mandatory
erpnext.patches.v14_0.delete_healthcare_doctypes
erpnext.patches.v13_0.update_category_in_ltds_certificate
erpnext.patches.v13_0.create_pan_field_for_india #2
erpnext.patches.v14_0.delete_hub_doctypes
erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit
erpnext.patches.v13_0.create_ksa_vat_custom_fields # 07-01-2022
erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
erpnext.patches.v14_0.migrate_crm_settings
erpnext.patches.v13_0.rename_ksa_qr_field
erpnext.patches.v13_0.wipe_serial_no_field_for_0_qty
erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021
erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
erpnext.patches.v13_0.update_tax_category_for_rcm
execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
erpnext.patches.v14_0.set_payroll_cost_centers
erpnext.patches.v13_0.agriculture_deprecation_warning
erpnext.patches.v14_0.delete_agriculture_doctypes
erpnext.patches.v13_0.hospitality_deprecation_warning
erpnext.patches.v13_0.update_exchange_rate_settings
erpnext.patches.v13_0.update_asset_quantity_field
erpnext.patches.v13_0.delete_bank_reconciliation_detail
[post_model_sync]
erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
erpnext.patches.v14_0.delete_einvoicing_doctypes
erpnext.patches.v14_0.delete_shopify_doctypes
erpnext.patches.v14_0.delete_hub_doctypes
erpnext.patches.v14_0.delete_hospitality_doctypes # 20-01-2022
erpnext.patches.v14_0.delete_agriculture_doctypes
erpnext.patches.v14_0.rearrange_company_fields
erpnext.patches.v14_0.update_leave_notification_template
erpnext.patches.v14_0.update_leave_notification_template
erpnext.patches.v13_0.update_sane_transfer_against

View File

@ -2,14 +2,28 @@ import frappe
def execute():
try:
frappe.db.sql("UPDATE `tabStock Ledger Entry` SET is_cancelled = 0 where is_cancelled in ('', NULL, 'No')")
frappe.db.sql("UPDATE `tabSerial No` SET is_cancelled = 0 where is_cancelled in ('', NULL, 'No')")
#handle type casting for is_cancelled field
module_doctypes = (
('stock', 'Stock Ledger Entry'),
('stock', 'Serial No'),
('accounts', 'GL Entry')
)
frappe.db.sql("UPDATE `tabStock Ledger Entry` SET is_cancelled = 1 where is_cancelled = 'Yes'")
frappe.db.sql("UPDATE `tabSerial No` SET is_cancelled = 1 where is_cancelled = 'Yes'")
for module, doctype in module_doctypes:
if (not frappe.db.has_column(doctype, "is_cancelled")
or frappe.db.get_column_type(doctype, "is_cancelled").lower() == "int(1)"
):
continue
frappe.reload_doc("stock", "doctype", "stock_ledger_entry")
frappe.reload_doc("stock", "doctype", "serial_no")
except Exception:
pass
frappe.db.sql("""
UPDATE `tab{doctype}`
SET is_cancelled = 0
where is_cancelled in ('', NULL, 'No')"""
.format(doctype=doctype))
frappe.db.sql("""
UPDATE `tab{doctype}`
SET is_cancelled = 1
where is_cancelled = 'Yes'"""
.format(doctype=doctype))
frappe.reload_doc(module, "doctype", frappe.scrub(doctype))

View File

@ -0,0 +1,13 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
def execute():
if frappe.db.exists('DocType', 'Bank Reconciliation Detail') and \
frappe.db.exists('DocType', 'Bank Clearance Detail'):
frappe.delete_doc("DocType", 'Bank Reconciliation Detail', force=1)

View File

@ -12,6 +12,7 @@ def execute():
for report in reports_to_delete:
if frappe.db.exists("Report", report):
delete_links_from_desktop_icons(report)
delete_auto_email_reports(report)
check_and_delete_linked_reports(report)
@ -22,3 +23,9 @@ def delete_auto_email_reports(report):
auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"])
for auto_email_report in auto_email_reports:
frappe.delete_doc("Auto Email Report", auto_email_report[0])
def delete_links_from_desktop_icons(report):
""" Check for one or multiple Desktop Icons and delete """
desktop_icons = frappe.db.get_values("Desktop Icon", {"_report": report}, ["name"])
for desktop_icon in desktop_icons:
frappe.delete_doc("Desktop Icon", desktop_icon[0])

View File

@ -0,0 +1,10 @@
import click
def execute():
click.secho(
"Hospitality domain is moved to a separate app and will be removed from ERPNext in version-14.\n"
"When upgrading to ERPNext version-14, please install the app to continue using the Hospitality domain: https://github.com/frappe/hospitality",
fg="yellow",
)

View File

@ -10,8 +10,15 @@ from erpnext.setup.install import add_non_standard_user_types
def execute():
doctype_dict = {
'projects': ['Timesheet'],
'payroll': ['Salary Slip', 'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission'],
'hr': ['Employee', 'Expense Claim', 'Leave Application', 'Attendance Request', 'Compensatory Leave Request']
'payroll': [
'Salary Slip', 'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission',
'Employee Benefit Application', 'Employee Benefit Claim'
],
'hr': [
'Employee', 'Expense Claim', 'Leave Application', 'Attendance Request', 'Compensatory Leave Request',
'Holiday List', 'Employee Advance', 'Training Program', 'Training Feedback',
'Shift Request', 'Employee Grievance', 'Employee Referral', 'Travel Request'
]
}
for module, doctypes in doctype_dict.items():

View File

@ -5,6 +5,9 @@ from erpnext.regional.india.setup import make_custom_fields
def execute():
if frappe.get_all('Company', filters = {'country': 'India'}):
frappe.reload_doc('accounts', 'doctype', 'POS Invoice')
frappe.reload_doc('accounts', 'doctype', 'POS Invoice Item')
make_custom_fields()
if not frappe.db.exists('Party Type', 'Donor'):

View File

@ -37,4 +37,4 @@ def execute():
jc.production_item = wo.production_item, jc.item_name = wo.item_name
WHERE
jc.work_order = wo.name and IFNULL(jc.production_item, "") = ""
""")
""")

View File

@ -0,0 +1,8 @@
import frappe
def execute():
if frappe.db.count('Asset'):
frappe.reload_doc("assets", "doctype", "Asset")
asset = frappe.qb.DocType('Asset')
frappe.qb.update(asset).set(asset.asset_quantity, 1).run()

View File

@ -0,0 +1,11 @@
import frappe
def execute():
bom = frappe.qb.DocType("BOM")
(frappe.qb
.update(bom)
.set(bom.transfer_material_against, "Work Order")
.where(bom.with_operations == 0)
).run()

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