瀏覽代碼

Initial

master
Anoop 2 年之前
當前提交
d0cb28e4da
共有 100 個檔案被更改,包括 5994 行新增0 行删除
  1. +6
    -0
      .gitignore
  2. +18
    -0
      MANIFEST.in
  3. +7
    -0
      README.md
  4. +1
    -0
      license.txt
  5. +5
    -0
      muezzin/__init__.py
  6. +0
    -0
      muezzin/api/__init__.py
  7. +76
    -0
      muezzin/api/property_contract.py
  8. +24
    -0
      muezzin/api/property_dashboard.py
  9. +0
    -0
      muezzin/config/__init__.py
  10. +14
    -0
      muezzin/config/desktop.py
  11. +11
    -0
      muezzin/config/docs.py
  12. +141
    -0
      muezzin/config/property_management.py
  13. +25
    -0
      muezzin/events/sales_invoice.py
  14. +30
    -0
      muezzin/fixtures/accounting_dimension.json
  15. +462
    -0
      muezzin/fixtures/custom_field.json
  16. +18
    -0
      muezzin/fixtures/property_setter.json
  17. +161
    -0
      muezzin/hooks.py
  18. +1
    -0
      muezzin/modules.txt
  19. +0
    -0
      muezzin/patches.txt
  20. +0
    -0
      muezzin/property_management/__init__.py
  21. +0
    -0
      muezzin/property_management/doctype/__init__.py
  22. +0
    -0
      muezzin/property_management/doctype/auction/__init__.py
  23. +31
    -0
      muezzin/property_management/doctype/auction/auction.js
  24. +118
    -0
      muezzin/property_management/doctype/auction/auction.json
  25. +10
    -0
      muezzin/property_management/doctype/auction/auction.py
  26. +10
    -0
      muezzin/property_management/doctype/auction/test_auction.py
  27. +0
    -0
      muezzin/property_management/doctype/auction_free_card/__init__.py
  28. +37
    -0
      muezzin/property_management/doctype/auction_free_card/auction_free_card.js
  29. +190
    -0
      muezzin/property_management/doctype/auction_free_card/auction_free_card.json
  30. +24
    -0
      muezzin/property_management/doctype/auction_free_card/auction_free_card.py
  31. +10
    -0
      muezzin/property_management/doctype/auction_free_card/test_auction_free_card.py
  32. +0
    -0
      muezzin/property_management/doctype/auction_unit/__init__.py
  33. +29
    -0
      muezzin/property_management/doctype/auction_unit/auction_unit.json
  34. +10
    -0
      muezzin/property_management/doctype/auction_unit/auction_unit.py
  35. +0
    -0
      muezzin/property_management/doctype/contract_annual_raise/__init__.py
  36. +43
    -0
      muezzin/property_management/doctype/contract_annual_raise/contract_annual_raise.json
  37. +10
    -0
      muezzin/property_management/doctype/contract_annual_raise/contract_annual_raise.py
  38. +0
    -0
      muezzin/property_management/doctype/contract_payments/__init__.py
  39. +92
    -0
      muezzin/property_management/doctype/contract_payments/contract_payments.json
  40. +10
    -0
      muezzin/property_management/doctype/contract_payments/contract_payments.py
  41. +0
    -0
      muezzin/property_management/doctype/court/__init__.py
  42. +8
    -0
      muezzin/property_management/doctype/court/court.js
  43. +52
    -0
      muezzin/property_management/doctype/court/court.json
  44. +10
    -0
      muezzin/property_management/doctype/court/court.py
  45. +10
    -0
      muezzin/property_management/doctype/court/test_court.py
  46. +0
    -0
      muezzin/property_management/doctype/discount_request/__init__.py
  47. +39
    -0
      muezzin/property_management/doctype/discount_request/discount_request.js
  48. +153
    -0
      muezzin/property_management/doctype/discount_request/discount_request.json
  49. +52
    -0
      muezzin/property_management/doctype/discount_request/discount_request.py
  50. +10
    -0
      muezzin/property_management/doctype/discount_request/test_discount_request.py
  51. +0
    -0
      muezzin/property_management/doctype/document/__init__.py
  52. +60
    -0
      muezzin/property_management/doctype/document/document.json
  53. +10
    -0
      muezzin/property_management/doctype/document/document.py
  54. +0
    -0
      muezzin/property_management/doctype/document_type/__init__.py
  55. +8
    -0
      muezzin/property_management/doctype/document_type/document_type.js
  56. +43
    -0
      muezzin/property_management/doctype/document_type/document_type.json
  57. +10
    -0
      muezzin/property_management/doctype/document_type/document_type.py
  58. +10
    -0
      muezzin/property_management/doctype/document_type/test_document_type.py
  59. +0
    -0
      muezzin/property_management/doctype/maintenance_ticket/__init__.py
  60. +47
    -0
      muezzin/property_management/doctype/maintenance_ticket/maintenance_ticket.js
  61. +173
    -0
      muezzin/property_management/doctype/maintenance_ticket/maintenance_ticket.json
  62. +10
    -0
      muezzin/property_management/doctype/maintenance_ticket/maintenance_ticket.py
  63. +10
    -0
      muezzin/property_management/doctype/maintenance_ticket/test_maintenance_ticket.py
  64. +0
    -0
      muezzin/property_management/doctype/opening_phase/__init__.py
  65. +8
    -0
      muezzin/property_management/doctype/opening_phase/opening_phase.js
  66. +43
    -0
      muezzin/property_management/doctype/opening_phase/opening_phase.json
  67. +10
    -0
      muezzin/property_management/doctype/opening_phase/opening_phase.py
  68. +10
    -0
      muezzin/property_management/doctype/opening_phase/test_opening_phase.py
  69. +0
    -0
      muezzin/property_management/doctype/property/__init__.py
  70. +58
    -0
      muezzin/property_management/doctype/property/property.js
  71. +499
    -0
      muezzin/property_management/doctype/property/property.json
  72. +10
    -0
      muezzin/property_management/doctype/property/property.py
  73. +10
    -0
      muezzin/property_management/doctype/property/test_property.py
  74. +0
    -0
      muezzin/property_management/doctype/property_contract/__init__.py
  75. +545
    -0
      opy).py
  76. +351
    -0
      muezzin/property_management/doctype/property_contract/property_contract.js
  77. +745
    -0
      muezzin/property_management/doctype/property_contract/property_contract.json
  78. +545
    -0
      muezzin/property_management/doctype/property_contract/property_contract.py
  79. +10
    -0
      muezzin/property_management/doctype/property_contract/test_property_contract.py
  80. +0
    -0
      muezzin/property_management/doctype/property_type/__init__.py
  81. +8
    -0
      muezzin/property_management/doctype/property_type/property_type.js
  82. +41
    -0
      muezzin/property_management/doctype/property_type/property_type.json
  83. +10
    -0
      muezzin/property_management/doctype/property_type/property_type.py
  84. +10
    -0
      muezzin/property_management/doctype/property_type/test_property_type.py
  85. +0
    -0
      muezzin/property_management/doctype/property_unit_detail/__init__.py
  86. +36
    -0
      muezzin/property_management/doctype/property_unit_detail/property_unit_detail.json
  87. +10
    -0
      muezzin/property_management/doctype/property_unit_detail/property_unit_detail.py
  88. +0
    -0
      muezzin/property_management/doctype/real_estate_offer/__init__.py
  89. +8
    -0
      muezzin/property_management/doctype/real_estate_offer/real_estate_offer.js
  90. +273
    -0
      muezzin/property_management/doctype/real_estate_offer/real_estate_offer.json
  91. +10
    -0
      muezzin/property_management/doctype/real_estate_offer/real_estate_offer.py
  92. +10
    -0
      muezzin/property_management/doctype/real_estate_offer/test_real_estate_offer.py
  93. +0
    -0
      muezzin/property_management/doctype/real_estate_order/__init__.py
  94. +8
    -0
      muezzin/property_management/doctype/real_estate_order/real_estate_order.js
  95. +275
    -0
      muezzin/property_management/doctype/real_estate_order/real_estate_order.json
  96. +10
    -0
      muezzin/property_management/doctype/real_estate_order/real_estate_order.py
  97. +10
    -0
      muezzin/property_management/doctype/real_estate_order/test_real_estate_order.py
  98. +0
    -0
      muezzin/property_management/doctype/reservation_repayment_schedule/__init__.py
  99. +82
    -0
      muezzin/property_management/doctype/reservation_repayment_schedule/reservation_repayment_schedule.json
  100. +10
    -0
      muezzin/property_management/doctype/reservation_repayment_schedule/reservation_repayment_schedule.py

+ 6
- 0
.gitignore 查看文件

@@ -0,0 +1,6 @@
.DS_Store
*.pyc
*.egg-info
*.swp
tags
property_management/docs/current

+ 18
- 0
MANIFEST.in 查看文件

@@ -0,0 +1,18 @@
include MANIFEST.in
include requirements.txt
include *.json
include *.md
include *.py
include *.txt
recursive-include property_management *.css
recursive-include property_management *.csv
recursive-include property_management *.html
recursive-include property_management *.ico
recursive-include property_management *.js
recursive-include property_management *.json
recursive-include property_management *.md
recursive-include property_management *.png
recursive-include property_management *.py
recursive-include property_management *.svg
recursive-include property_management *.txt
recursive-exclude property_management *.pyc

+ 7
- 0
README.md 查看文件

@@ -0,0 +1,7 @@
## Property Management

Property Management

#### License

MIT

+ 1
- 0
license.txt 查看文件

@@ -0,0 +1 @@
License: MIT

+ 5
- 0
muezzin/__init__.py 查看文件

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

__version__ = '0.0.1'


+ 0
- 0
muezzin/api/__init__.py 查看文件


+ 76
- 0
muezzin/api/property_contract.py 查看文件

@@ -0,0 +1,76 @@
import frappe
import json

@frappe.whitelist()
def change_contract_status(doc, status):
"""
Validates if property already on rent between the from and to dates
"""
doc = json.loads(doc)

if status == "Hold":
doc = frappe.get_doc("Property Contract", doc["name"])
doc.status = status
doc.save()

unit_doc = frappe.get_doc("Unit", doc.unit)
unit_doc.status = "Vacant"
unit_doc.save()
return

# Get Property Contaract if from date is between new from and new to date
filters = {
"rent_start_date": ["between", [doc["rent_start_date"], doc["rent_end_date"]]],
"docstatus": 1,
"unit_name": doc["unit_name"],
"status": ["!=", "Hold"]
}
property_contract = frappe.get_list("Property Contract",
fields = ["name"],
filters = filters)

if len(property_contract)> 0:
frappe.throw(f"This property unit already contain a contract <a href='#Form/Property%20Contract/{property_contract[0].name}'>{property_contract[0].name}</a> that not ended yet.")


# Get Property Contaract if to date is between new from and new to date
filters = {
"rent_end_date": ["between", [doc["rent_start_date"], doc["rent_end_date"]]],
"docstatus": 1,
"unit_name": doc["unit_name"],
"status": ["!=", "Hold"]
}
property_contract = frappe.get_list("Property Contract",
fields = ["name"],
filters = filters)

if len(property_contract)> 0:
frappe.throw(f"This property unit already contain a contract <a href='#Form/Property%20Contract/{property_contract[0].name}'>{property_contract[0].name}</a> that not ended yet.")


# Get Property Contaract if from date is after new from date and to date is before new to date
filters = {
"rent_start_date": ["<=", doc["rent_start_date"]],
"rent_end_date": [">=", doc["rent_end_date"]],
"docstatus": 1,
"unit_name": doc["unit_name"],
"status": ["!=", "Hold"]
}
property_contract = frappe.get_list("Property Contract",
fields = ["name"],
filters = filters)

if len(property_contract)> 0:
frappe.throw(f"This property unit already contain a contract <a href='#Form/Property%20Contract/{property_contract[0].name}'>{property_contract[0].name}</a> that not ended yet.")


doc = frappe.get_doc("Property Contract", doc["name"])
doc.status = status
doc.save()

unit_doc = frappe.get_doc("Unit", doc.unit_name)
unit_doc.status = "Leased"
unit_doc.save()

+ 24
- 0
muezzin/api/property_dashboard.py 查看文件

@@ -0,0 +1,24 @@
import frappe


@frappe.whitelist()
def get_tiles_data():
'''
function will use to get property dashboard's tiles data
'''

properties = frappe.db.count("Property")
units = frappe.db.count("Unit")
sold_units = frappe.db.count("Unit", {"status": "Sold"})
rented_units = frappe.db.count("Unit", {"status": "Rented"})
available_units = frappe.db.count("Unit", {"status": "Available"})

data = {
"total_property": properties,
"total_unit": units,
"total_sold_unit": sold_units,
"total_rented_unit": rented_units,
"total_available_unit": available_units,
}

return data

+ 0
- 0
muezzin/config/__init__.py 查看文件


+ 14
- 0
muezzin/config/desktop.py 查看文件

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from frappe import _

def get_data():
return [
{
"module_name": "Property Management",
"color": "grey",
"icon": "octicon octicon-file-directory",
"type": "module",
"label": _("Property Management")
}
]

+ 11
- 0
muezzin/config/docs.py 查看文件

@@ -0,0 +1,11 @@
"""
Configuration for docs
"""

# source_link = "https://github.com/[org_name]/property_management"
# docs_base_url = "https://[org_name].github.io/property_management"
# headline = "App that does everything"
# sub_heading = "Yes, you got that right the first time, everything"

def get_context(context):
context.brand_html = "Property Management"

+ 141
- 0
muezzin/config/property_management.py 查看文件

@@ -0,0 +1,141 @@
from __future__ import unicode_literals
from frappe import _


def get_data():
return [
{
"label": _("Services"),
"items": [
{
"type": "doctype",
"name": "Maintenance Ticket",
"onboard": 1
},
{
"type": "doctype",
"name": "Property Contract",
"onboard": 1
},
{
"type": "doctype",
"name": "Reservations",
"onboard": 1
},
{
"type": "doctype",
"name": "Auction",
"onboard": 1
},
{
"type": "doctype",
"name": "Auction Free Card",
"onboard": 1
},
{
"type": "doctype",
"name": "Real Estate Offer",
"onboard": 1
},
{
"type": "doctype",
"name": "Real Estate Order",
"onboard": 1
}
]
},
{
"label": _("Setup"),
"items": [
{
"type": "doctype",
"name": "Unit",
"onboard": 1
},
{
"type": "doctype",
"name": "Property",
"onboard": 1
},
{
"type": "doctype",
"name": "Unit Type",
"onboard": 1
},
{
"type": "doctype",
"name": "Property Type",
"onboard": 1
},
{
"type": "doctype",
"name": "Unit Activity",
"onboard": 1
},
{
"type": "doctype",
"name": "Opening Phase",
"onboard": 1
},
]
},
{
"label": _("People"),
"items": [
{
"type": "doctype",
"name": "User",
"onboard": 1
},
{
"type": "doctype",
"name": "Customer",
"onboard": 1
}
]
},
{
"label": _("Reports"),
"items": [
{
"type": "report",
"is_query_report": True,
"name": "Rent Roll",
"doctype": "Property Contract",
"onboard": 1
},
{
"type": "report",
"is_query_report": True,
"name": "Service Charge Roll",
"doctype": "Property Contract",
"onboard": 1
},
{
"type": "report",
"is_query_report": True,
"name": "Sold Unit",
"doctype": "Unit",
"onboard": 1
},
{
"type": "report",
"is_query_report": True,
"name": "Property Management Revenue",
"doctype": "Property Contract",
"onboard": 1
}
]
},
{
"label": _("Dashboards"),
"items": [
{
"type": "page",
"name": "property-dashboard",
"label": _("Property Dashboard"),
"onboard": 1
}
]
},
]

+ 25
- 0
muezzin/events/sales_invoice.py 查看文件

@@ -0,0 +1,25 @@
import frappe

def submit(doc, method):
if hasattr(doc, 'property') and doc.property:
prop = frappe.get_doc('Property', doc.property)
prop.customer = doc.customer
prop.customer_name = doc.customer_name
prop.save()
if hasattr(doc, 'unit') and doc.unit:
unit = frappe.get_doc('Unit', doc.unit)
unit.customer = doc.customer
unit.customer_name = doc.customer_name
unit.save()

def cancel(doc, method):
if hasattr(doc, 'property') and doc.property:
prop = frappe.get_doc('Property', doc.property)
prop.customer = None
prop.customer_name = None
prop.save()
if hasattr(doc, 'unit') and doc.unit:
unit = frappe.get_doc('Unit', doc.unit)
unit.customer = None
unit.customer_name = None
unit.save()

+ 30
- 0
muezzin/fixtures/accounting_dimension.json 查看文件

@@ -0,0 +1,30 @@
[
{
"dimension_defaults": [],
"disabled": 0,
"docstatus": 0,
"doctype": "Accounting Dimension",
"document_type": "Unit",
"fieldname": "unit",
"label": "Unit",
"modified": "2021-02-01 15:29:00.082653",
"name": "Unit",
"parent": null,
"parentfield": null,
"parenttype": null
},
{
"dimension_defaults": [],
"disabled": 0,
"docstatus": 0,
"doctype": "Accounting Dimension",
"document_type": "Property",
"fieldname": "property",
"label": "Property",
"modified": "2021-02-01 15:29:09.706024",
"name": "Property",
"parent": null,
"parentfield": null,
"parenttype": null
}
]

+ 462
- 0
muezzin/fixtures/custom_field.json 查看文件

@@ -0,0 +1,462 @@
[
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": "eval:doc.customer_type == \"Company\"",
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Customer",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "company_name_en",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "customer_name",
"label": "Company Name en",
"length": 0,
"modified": "2021-04-12 01:00:41.963015",
"name": "Customer-company_name_en",
"no_copy": 0,
"options": null,
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Lead",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "sales_persons",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "email_id",
"label": "Sales Persons",
"length": 0,
"modified": "2021-10-24 01:56:12.828482",
"name": "Lead-sales_persons",
"no_copy": 0,
"options": "Sales Person",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Customer",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "customer_documents",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "disabled",
"label": "Customer Documents",
"length": 0,
"modified": "2021-12-28 00:44:27.424715",
"name": "Customer-customer_documents",
"no_copy": 0,
"options": null,
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Sales Invoice",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "property_contract",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "project",
"label": "Property Contract",
"length": 0,
"modified": "2021-06-18 02:01:44.898355",
"name": "Sales Invoice-property_contract",
"no_copy": 0,
"options": "Property Contract",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Customer",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "documents",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "customer_documents",
"label": "Documents",
"length": 0,
"modified": "2021-12-28 00:44:27.946523",
"name": "Customer-documents",
"no_copy": 0,
"options": "Document",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Journal Entry",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "accounting_dimensions",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "select_print_heading",
"label": "Accounting Dimensions",
"length": 0,
"modified": "2021-02-01 17:24:28.183421",
"name": "Journal Entry-accounting_dimensions",
"no_copy": 0,
"options": null,
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Journal Entry",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "unit",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "accounting_dimensions",
"label": "Unit",
"length": 0,
"modified": "2021-02-01 17:24:28.342902",
"name": "Journal Entry-unit",
"no_copy": 0,
"options": "Unit",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Journal Entry",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "property",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "unit",
"label": "Property",
"length": 0,
"modified": "2021-02-01 17:24:28.545797",
"name": "Journal Entry-property",
"no_copy": 0,
"options": "Property",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Journal Entry",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "column_break_45",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "property",
"label": "",
"length": 0,
"modified": "2021-02-01 17:24:28.796911",
"name": "Journal Entry-column_break_45",
"no_copy": 0,
"options": "",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Journal Entry",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "cost_center",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"insert_after": "column_break_45",
"label": "Cost Center",
"length": 0,
"modified": "2021-02-01 17:24:28.955064",
"name": "Journal Entry-cost_center",
"no_copy": 0,
"options": "Cost Center",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
}
]

+ 18
- 0
muezzin/fixtures/property_setter.json 查看文件

@@ -0,0 +1,18 @@
[
{
"default_value": null,
"doc_type": "Customer",
"docstatus": 0,
"doctype": "Property Setter",
"doctype_or_field": "DocField",
"field_name": "customer_name",
"modified": "2021-04-12 01:03:25.733494",
"name": "Customer-customer_name-label",
"parent": null,
"parentfield": null,
"parenttype": null,
"property": "label",
"property_type": "Data",
"value": "Legal Name"
}
]

+ 161
- 0
muezzin/hooks.py 查看文件

@@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from . import __version__ as app_version

app_name = "muezzin"
app_title = "Muezzin"
app_publisher = "Havenir Solutions"
app_description = "Property Management App for Muezzin"
app_icon = "octicon octicon-file-directory"
app_color = "grey"
app_email = "info@havenir.com"
app_license = "MIT"

# Includes in <head>
# ------------------

# include js, css files in header of desk.html
app_include_css = "/assets/css/muezzin.css"
# app_include_js = "/assets/muezzin/js/muezzin.js"

# include js, css files in header of web template
# web_include_css = "/assets/muezzin/css/muezzin.css"
# web_include_js = "/assets/muezzin/js/muezzin.js"

# include js in page
# page_js = {"page" : "public/js/file.js"}

# include js in doctype views
doctype_js = {
"Payment Entry": "public/js/payment_entry.js"
}

# doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
# doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"}

# Home Pages
# ----------

# application home page (will override Website Settings)
# home_page = "login"

# website user home page (by Role)
# role_home_page = {
# "Role": "home_page"
# }

# Website user home page (by function)
# get_website_user_home_page = "muezzin.utils.get_home_page"

# Generators
# ----------

# automatically create page for each record of this doctype
# website_generators = ["Web Page"]

# Installation
# ------------

# before_install = "muezzin.install.before_install"
# after_install = "muezzin.install.after_install"

# Desk Notifications
# ------------------
# See frappe.core.notifications.get_notification_config

# notification_config = "muezzin.notifications.get_notification_config"

# Permissions
# -----------
# Permissions evaluated in scripted ways

# permission_query_conditions = {
# "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions",
# }
#
# has_permission = {
# "Event": "frappe.desk.doctype.event.event.has_permission",
# }

# Document Events
# ---------------
# Hook on document methods and events

doc_events = {
"Sales Invoice": {
"on_submit": "muezzin.events.sales_invoice.submit",
"on_cancel": "muezzin.events.sales_invoice.cancel"
}
}

# Scheduled Tasks
# ---------------

scheduler_events = {
# "all": [
# "muezzin.tasks.all"
# ],
"daily": [
# "muezzin.property_management.doctype.property_contract.property_contract.create_sales_invoice",
"muezzin.property_management.doctype.property_contract.property_contract.update_unit_status"
],
"hourly": [
"muezzin.property_management.doctype.property_contract.property_contract.update_unit_status_basd_on_reservation"
],
# "weekly": [
# "muezzin.tasks.weekly"
# ]
# "monthly": [
# "muezzin.tasks.monthly"
# ]
}

# Testing
# -------

# before_tests = "muezzin.install.before_tests"

# Overriding Methods
# ------------------------------
#
# override_whitelisted_methods = {
# "frappe.desk.doctype.event.event.get_events": "muezzin.event.get_events"
# }
#
# each overriding function accepts a `data` argument;
# generated from the base implementation of the doctype dashboard,
# along with any modifications made in other Frappe apps
# override_doctype_dashboards = {
# "Task": "muezzin.task.get_dashboard_data"
# }
fixtures = [{
"dt": "Custom Field",
"filters": [["name", "in", [
'Journal Entry-accounting_dimensions',
'Journal Entry-unit',
'Journal Entry-property',
'Journal Entry-column_break_45',
'Journal Entry-cost_center',
'Customer-company_name_en',
'Sales Invoice-property_contract',
'Lead-sales_persons',
'Customer-documents',
'Customer-customer_documents'
]]]
},
{
"dt": "Property Setter",
"filters": [["name", "in", [
'Customer-customer_name-l abel',
]]]
},
{
"dt": "Accounting Dimension",
"filters": [["name", "in", [
'Property',
'Unit'
]]]
}

]

+ 1
- 0
muezzin/modules.txt 查看文件

@@ -0,0 +1 @@
Property Management

+ 0
- 0
muezzin/patches.txt 查看文件


+ 0
- 0
muezzin/property_management/__init__.py 查看文件


+ 0
- 0
muezzin/property_management/doctype/__init__.py 查看文件


+ 0
- 0
muezzin/property_management/doctype/auction/__init__.py 查看文件


+ 31
- 0
muezzin/property_management/doctype/auction/auction.js 查看文件

@@ -0,0 +1,31 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Auction', {
refresh: function(frm) {
frm.set_query('property', () => {
return {
filters: {
property_type: frm.doc.property_type
}
}
})
},

property_type: function(frm) {
frm.doc.property = null;
frm.refresh_field('property');
},

property: function(frm) {
if (frm.doc.property) {
frappe.db.get_value('Property', frm.doc.property, 'property_type')
.then( r=> {
if (r.message) {
frm.doc.property_type = r.message.property_type;
frm.refresh_field('property_type');
}
})
}
}
});

+ 118
- 0
muezzin/property_management/doctype/auction/auction.json 查看文件

@@ -0,0 +1,118 @@
{
"autoname": "format:{auction_subject}-{###}",
"creation": "2021-02-22 18:46:54.748490",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"auction_subject",
"property_type",
"property",
"unit",
"attach_a_file",
"column_break_4",
"date",
"number_of_pieces",
"auction_type",
"court",
"amended_from"
],
"fields": [
{
"fieldname": "auction_subject",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Auction Subject",
"reqd": 1
},
{
"fieldname": "property_type",
"fieldtype": "Link",
"label": "Property Type",
"options": "Property Type"
},
{
"fieldname": "property",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Property",
"options": "Property",
"reqd": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"default": "Now",
"fieldname": "date",
"fieldtype": "Date",
"label": "Date"
},
{
"fieldname": "number_of_pieces",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Number of Pieces",
"reqd": 1
},
{
"fieldname": "auction_type",
"fieldtype": "Select",
"label": "Auction Type",
"options": "Ministry of Justice\nIndividual"
},
{
"fieldname": "court",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Court",
"options": "Court",
"reqd": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Auction",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "attach_a_file",
"fieldtype": "Attach",
"label": "Attach a File"
},
{
"fieldname": "unit",
"fieldtype": "Table MultiSelect",
"label": "Unit",
"options": "Auction Unit",
"reqd": 1
}
],
"is_submittable": 1,
"modified": "2021-02-25 11:58:26.923370",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Auction",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/auction/auction.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class Auction(Document):
pass

+ 10
- 0
muezzin/property_management/doctype/auction/test_auction.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestAuction(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/auction_free_card/__init__.py 查看文件


+ 37
- 0
muezzin/property_management/doctype/auction_free_card/auction_free_card.js 查看文件

@@ -0,0 +1,37 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Auction Free Card', {
refresh: function(frm) {
frm.set_query('units', () => {
if (frm.doc.auction) {
return {
query: 'muezzin.property_management.doctype.auction_free_card.auction_free_card.get_unit',
filters: {
'parent': frm.doc.auction
}
}
} else {
return {
filters: {
"parent": 'Empty'
}
}
}
})
},

auction: function(frm) {
if(frm.doc.auction) {
frm.call('set_unit')
.then( r => {
refresh_field('units');
})
} else {
frm.doc.units = [];
frm.doc.property_type = null;
frm.doc.property = null;
frm.refresh_fields('units', 'property', 'property_type');
}
}
});

+ 190
- 0
muezzin/property_management/doctype/auction_free_card/auction_free_card.json 查看文件

@@ -0,0 +1,190 @@
{
"autoname": "format:AFC-{auction}-{###}",
"creation": "2021-02-23 14:11:09.113084",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"auction",
"property",
"customer",
"column_break_4",
"units",
"property_type",
"mobile_number",
"national_id",
"section_break_9",
"bank",
"check_number",
"check_value",
"vat_on_commission",
"quest_value",
"total_quest",
"column_break_16",
"opening_price",
"plan",
"notes",
"tax_on_property",
"attach_files",
"amended_from"
],
"fields": [
{
"fieldname": "auction",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Auction",
"options": "Auction",
"reqd": 1
},
{
"fetch_from": "auction.property",
"fieldname": "property",
"fieldtype": "Data",
"label": "Property",
"read_only": 1
},
{
"fieldname": "mobile_number",
"fieldtype": "Data",
"label": "Mobile Number"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"fetch_from": "auction.property_type",
"fieldname": "property_type",
"fieldtype": "Data",
"label": "Property Type",
"read_only": 1
},
{
"fieldname": "units",
"fieldtype": "Table MultiSelect",
"label": "Units",
"options": "Auction Unit",
"reqd": 1
},
{
"fieldname": "customer",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Customer",
"options": "Customer",
"reqd": 1
},
{
"fieldname": "national_id",
"fieldtype": "Data",
"label": "National Id"
},
{
"fieldname": "section_break_9",
"fieldtype": "Section Break"
},
{
"fieldname": "bank",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Bank",
"options": "Bank",
"reqd": 1
},
{
"fieldname": "check_number",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Check Number",
"reqd": 1
},
{
"fieldname": "check_value",
"fieldtype": "Float",
"label": "Check Value",
"reqd": 1
},
{
"fieldname": "vat_on_commission",
"fieldtype": "Float",
"label": "Vat On Commission",
"reqd": 1
},
{
"fieldname": "total_quest",
"fieldtype": "Float",
"label": "Total Quest",
"reqd": 1
},
{
"default": "0",
"fieldname": "tax_on_property",
"fieldtype": "Check",
"label": "Tax on Property"
},
{
"fieldname": "column_break_16",
"fieldtype": "Column Break"
},
{
"fieldname": "opening_price",
"fieldtype": "Float",
"label": "Opening Price"
},
{
"fieldname": "quest_value",
"fieldtype": "Float",
"label": "Quest Value",
"reqd": 1
},
{
"fieldname": "plan",
"fieldtype": "Data",
"label": "Plan",
"reqd": 1
},
{
"fieldname": "notes",
"fieldtype": "Data",
"label": "Notes"
},
{
"fieldname": "attach_files",
"fieldtype": "Attach",
"label": "Attach Files"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Auction Free Card",
"print_hide": 1,
"read_only": 1
}
],
"is_submittable": 1,
"modified": "2021-02-25 11:57:24.514506",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Auction Free Card",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 24
- 0
muezzin/property_management/doctype/auction_free_card/auction_free_card.py 查看文件

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe.model.document import Document

class AuctionFreeCard(Document):
def set_unit(self):
result = frappe.db.get_list('Auction Unit', {'parent': self.auction}, 'unit')
for row in result:
self.append('units', {
'unit': row.unit
})

@frappe.whitelist()
def get_unit(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select unit
from `tabAuction Unit`
where
parent = {auction}"""
.format(auction = frappe.db.escape(filters.get("auction"))
))

+ 10
- 0
muezzin/property_management/doctype/auction_free_card/test_auction_free_card.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestAuctionFreeCard(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/auction_unit/__init__.py 查看文件


+ 29
- 0
muezzin/property_management/doctype/auction_unit/auction_unit.json 查看文件

@@ -0,0 +1,29 @@
{
"creation": "2021-02-24 18:10:02.718182",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"unit"
],
"fields": [
{
"fieldname": "unit",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Unit",
"options": "Unit"
}
],
"istable": 1,
"modified": "2021-02-24 18:35:03.788544",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Auction Unit",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/auction_unit/auction_unit.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class AuctionUnit(Document):
pass

+ 0
- 0
muezzin/property_management/doctype/contract_annual_raise/__init__.py 查看文件


+ 43
- 0
muezzin/property_management/doctype/contract_annual_raise/contract_annual_raise.json 查看文件

@@ -0,0 +1,43 @@
{
"creation": "2021-02-02 12:45:10.043925",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"date",
"annual_raise_type",
"annual_raise_amount"
],
"fields": [
{
"fieldname": "date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Date"
},
{
"fieldname": "annual_raise_type",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Annual Raise Type",
"options": "\nAmount\nPercentage"
},
{
"fieldname": "annual_raise_amount",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Annual Raise Amount"
}
],
"istable": 1,
"modified": "2021-06-30 03:36:03.351219",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Contract Annual Raise",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/contract_annual_raise/contract_annual_raise.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class ContractAnnualRaise(Document):
pass

+ 0
- 0
muezzin/property_management/doctype/contract_payments/__init__.py 查看文件


+ 92
- 0
muezzin/property_management/doctype/contract_payments/contract_payments.json 查看文件

@@ -0,0 +1,92 @@
{
"creation": "2021-02-02 13:38:41.441710",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"date",
"amount",
"reason",
"invoice",
"create_invoice",
"discount_applied",
"discount_refrence",
"remarks"
],
"fields": [
{
"columns": 1,
"fieldname": "date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Date",
"read_only": 1
},
{
"allow_on_submit": 1,
"columns": 1,
"fieldname": "amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Amount",
"options": "tenant_name_currency"
},
{
"columns": 2,
"fieldname": "reason",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Reason"
},
{
"columns": 2,
"fieldname": "invoice",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Invoice",
"options": "Sales Invoice",
"read_only": 1
},
{
"allow_on_submit": 1,
"columns": 1,
"fieldname": "create_invoice",
"fieldtype": "Button",
"in_list_view": 1,
"label": "Create Invoice"
},
{
"columns": 2,
"fieldname": "remarks",
"fieldtype": "Small Text",
"in_list_view": 1,
"label": "Remarks"
},
{
"columns": 1,
"default": "0",
"fieldname": "discount_applied",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Discount Applied",
"read_only": 1
},
{
"fieldname": "discount_refrence",
"fieldtype": "Data",
"label": "Discount Refrence",
"read_only": 1
}
],
"istable": 1,
"modified": "2021-12-25 18:08:34.469555",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Contract Payments",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/contract_payments/contract_payments.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class ContractPayments(Document):
pass

+ 0
- 0
muezzin/property_management/doctype/court/__init__.py 查看文件


+ 8
- 0
muezzin/property_management/doctype/court/court.js 查看文件

@@ -0,0 +1,8 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Court', {
// refresh: function(frm) {

// }
});

+ 52
- 0
muezzin/property_management/doctype/court/court.json 查看文件

@@ -0,0 +1,52 @@
{
"autoname": "field:court_name",
"creation": "2021-02-22 18:46:46.022348",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"court_name",
"amended_from"
],
"fields": [
{
"fieldname": "court_name",
"fieldtype": "Data",
"label": "Court Name",
"unique": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Court",
"print_hide": 1,
"read_only": 1
}
],
"is_submittable": 1,
"modified": "2021-02-22 18:46:46.022348",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Court",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/court/court.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class Court(Document):
pass

+ 10
- 0
muezzin/property_management/doctype/court/test_court.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestCourt(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/discount_request/__init__.py 查看文件


+ 39
- 0
muezzin/property_management/doctype/discount_request/discount_request.js 查看文件

@@ -0,0 +1,39 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Discount Request', {
refresh: function (frm) {
get_brand_names(frm)
},
onload: function (frm) {
get_brand_names(frm)
},
customer: function (frm) {
get_brand_names(frm)
frm.set_query('property_contract', function (doc) {
return {
filters: {
renter: frm.doc.customer,
docstatus: 1
}
}
});
}
});


let get_brand_names = function (frm) {
if (frm.doc.customer) {
frappe.call({
method: "get_brand_names",
doc: frm.doc,
callback: function (data) {
if (data.message) {
let options = data.message;
let options_new = options.join("\n");
frm.set_df_property('brand_name', 'options', options_new);
}
}
})
}
}

+ 153
- 0
muezzin/property_management/doctype/discount_request/discount_request.json 查看文件

@@ -0,0 +1,153 @@
{
"autoname": "format:{property_contract}-{YYYY}-{#####}",
"creation": "2021-12-25 15:32:33.360291",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"customer",
"customer_name",
"brand_name",
"column_break_4",
"start_date",
"end_date",
"section_break_7",
"property_contract",
"unit_name",
"column_break_10",
"apply_discount_on",
"discount_type",
"percentage",
"amount",
"section_break_14",
"description",
"amended_from"
],
"fields": [
{
"fieldname": "customer",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Customer",
"options": "Customer",
"reqd": 1
},
{
"fetch_from": "customer.customer_name",
"fieldname": "customer_name",
"fieldtype": "Read Only",
"label": "Customer Name"
},
{
"fieldname": "brand_name",
"fieldtype": "Select",
"label": "Tenant Brand Name"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"fieldname": "start_date",
"fieldtype": "Date",
"label": "Start Date",
"reqd": 1
},
{
"fieldname": "end_date",
"fieldtype": "Date",
"label": "End Date",
"reqd": 1
},
{
"fieldname": "section_break_7",
"fieldtype": "Section Break"
},
{
"fieldname": "property_contract",
"fieldtype": "Link",
"label": "Property Contract",
"options": "Property Contract"
},
{
"fetch_from": "property_contract.unit",
"fieldname": "unit_name",
"fieldtype": "Link",
"label": "Unit Name",
"options": "Unit",
"read_only": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Discount Request",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "column_break_10",
"fieldtype": "Column Break"
},
{
"fieldname": "discount_type",
"fieldtype": "Select",
"label": "Discount Type",
"options": "\nPercentage\nAmount"
},
{
"depends_on": "eval:doc.discount_type=='Percentage'",
"fieldname": "percentage",
"fieldtype": "Percent",
"label": "Percentage"
},
{
"depends_on": "eval:doc.discount_type=='Amount'",
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount"
},
{
"fieldname": "section_break_14",
"fieldtype": "Section Break"
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Description"
},
{
"fieldname": "apply_discount_on",
"fieldtype": "Select",
"label": "Apply Discount On",
"options": "\nRent\nServices Charges\nAll",
"reqd": 1
}
],
"is_submittable": 1,
"modified": "2021-12-25 16:28:30.028665",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Discount Request",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 1,
"track_views": 1
}

+ 52
- 0
muezzin/property_management/doctype/discount_request/discount_request.py 查看文件

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.utils import flt


class DiscountRequest(Document):
def on_submit(self):
self.add_remove_discount(type='Add')

def on_cancel(self):
self.add_remove_discount(type='Remove')

def get_brand_names(self):
list_items = []
brands = frappe.get_all("Tenant Brand Name", filters={"parent": self.customer}, fields=['tenant_brand_name'],
order_by="tenant_brand_name")
if brands:
for item in brands:
list_items.append(item["tenant_brand_name"])

return list_items

def add_remove_discount(self, type):
filters = {"parent": self.property_contract, "date": ["between", [self.start_date, self.end_date]],
"invoice": ["=", '']}
if self.apply_discount_on != "All":
filters['reason'] = self.apply_discount_on
payments = frappe.db.get_all("Contract Payments", filters=filters, order_by="date")
for row in payments:
doc = frappe.get_doc("Contract Payments", row["name"])
if type == 'Add':
doc.amount -= (
((flt(self.percentage) / 100) * flt(doc.amount)) if self.discount_type == 'Percentage' else flt(
self.amount))
doc.remarks = ' تم تطبيق خصم على هذا الشهريقيمة <br>' + str((((flt(self.percentage) / 100) * flt(
doc.amount)) if self.discount_type == 'Percentage' else flt(self.amount))) + "<br>" + str(self.name)
doc.discount_applied = 1
doc.discount_refrence = self.name
else:
doc.amount = (( (flt(doc.amount) * 100) / flt(100 - flt(self.percentage)) ) if self.discount_type == 'Percentage' else (flt(self.amount)+flt(doc.amount)))
doc.remarks = ''
doc.discount_applied = 0
doc.discount_refrence = ''

doc.flags.ignore_validate_update_after_submit = True
doc.save()
frappe.db.commit()

+ 10
- 0
muezzin/property_management/doctype/discount_request/test_discount_request.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestDiscountRequest(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/document/__init__.py 查看文件


+ 60
- 0
muezzin/property_management/doctype/document/document.json 查看文件

@@ -0,0 +1,60 @@
{
"creation": "2021-12-28 00:43:37.531121",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"title",
"document_type",
"attach",
"start_date",
"end_date"
],
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Title",
"reqd": 1
},
{
"fieldname": "document_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Document Type",
"options": "Document Type",
"reqd": 1
},
{
"fieldname": "attach",
"fieldtype": "Attach",
"in_list_view": 1,
"label": "Attach",
"reqd": 1
},
{
"fieldname": "start_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Start Date"
},
{
"fieldname": "end_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "End Date"
}
],
"istable": 1,
"modified": "2021-12-28 00:45:09.880101",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Document",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/document/document.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class Document(Document):
pass

+ 0
- 0
muezzin/property_management/doctype/document_type/__init__.py 查看文件


+ 8
- 0
muezzin/property_management/doctype/document_type/document_type.js 查看文件

@@ -0,0 +1,8 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Document Type', {
// refresh: function(frm) {

// }
});

+ 43
- 0
muezzin/property_management/doctype/document_type/document_type.json 查看文件

@@ -0,0 +1,43 @@
{
"autoname": "field:type",
"creation": "2021-12-28 00:40:08.546784",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"type"
],
"fields": [
{
"fieldname": "type",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Type",
"reqd": 1,
"unique": 1
}
],
"modified": "2021-12-28 00:40:14.109297",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Document Type",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/document_type/document_type.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class DocumentType(Document):
pass

+ 10
- 0
muezzin/property_management/doctype/document_type/test_document_type.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestDocumentType(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/maintenance_ticket/__init__.py 查看文件


+ 47
- 0
muezzin/property_management/doctype/maintenance_ticket/maintenance_ticket.js 查看文件

@@ -0,0 +1,47 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Maintenance Ticket', {
refresh: function(frm) {
queries(frm);
},

onload_post_render(frm) {
set_location(frm);
},

property(frm) {
if (frm.doc.property) {
// setTimeout(set_location(frm), 500);
} else {
frm.doc.unit = null;
frm.doc.property_location = null;
frm.doc.latitude = null;
frm.doc.longitude = null;
frm.refresh_fields(['unit', 'property_location', 'latitude', 'longitude']);
}
},
property_location(frm) {
set_location(frm);
}
});

function queries(frm) {
frm.set_query('unit', () => {
return {
filters: {
property: frm.doc.property
}
}
})
}

// function set_location(frm) {
// if (!frm.doc.property_location && frm.doc.latitude && frm.doc.longitude) {
// frm.fields_dict.property_location.map.setView([frm.doc.latitude, frm.doc.longitude], 13);
// }
// else {
// frm.doc.latitude = frm.fields_dict.property_location.map.getCenter()['lat'];
// frm.doc.longitude = frm.fields_dict.property_location.map.getCenter()['lng'];
// }
// }

+ 173
- 0
muezzin/property_management/doctype/maintenance_ticket/maintenance_ticket.json 查看文件

@@ -0,0 +1,173 @@
{
"autoname": "naming_series:",
"creation": "2021-01-15 17:58:51.656150",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"property",
"unit",
"category",
"property_location",
"column_break_5",
"client",
"mobile_number",
"service_provider",
"section_break_11",
"notes",
"section_break_13",
"latitude",
"responsible_user",
"column_break_15",
"longitude",
"send_sms_alert_to_responsible_user",
"section_break_18",
"location",
"amended_from"
],
"fields": [
{
"fieldname": "naming_series",
"fieldtype": "Data",
"label": "Serial Number",
"options": "MT-"
},
{
"fieldname": "property",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Property",
"options": "Property",
"reqd": 1
},
{
"fieldname": "category",
"fieldtype": "Select",
"label": "Category",
"options": "Electrical Maintenance\nWater Maintenance\nA / C Maintenance\nElevators\nCleaning\nOther"
},
{
"fieldname": "mobile_number",
"fieldtype": "Data",
"label": "Mobile Number"
},
{
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
{
"fieldname": "service_provider",
"fieldtype": "Link",
"label": "Service Provider",
"options": "Supplier"
},
{
"fieldname": "client",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Client",
"options": "Customer",
"reqd": 1
},
{
"fetch_from": "property.property_location",
"fieldname": "property_location",
"fieldtype": "Data",
"label": "Property Location",
"read_only": 1
},
{
"fieldname": "notes",
"fieldtype": "Small Text",
"label": "Notes"
},
{
"default": "0",
"fieldname": "send_sms_alert_to_responsible_user",
"fieldtype": "Check",
"label": "Send SMS Alert to Responsible User"
},
{
"fetch_from": "property.latitude",
"fieldname": "latitude",
"fieldtype": "Data",
"label": "Latitude",
"read_only": 1
},
{
"fieldname": "column_break_15",
"fieldtype": "Column Break"
},
{
"fieldname": "responsible_user",
"fieldtype": "Link",
"label": "Responsible User",
"options": "User"
},
{
"fetch_from": "property.longitude",
"fieldname": "longitude",
"fieldtype": "Data",
"label": "Longitude",
"read_only": 1
},
{
"fieldname": "section_break_18",
"fieldtype": "Section Break"
},
{
"fieldname": "location",
"fieldtype": "Geolocation",
"label": "Location"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Maintenance Ticket",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "unit",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Unit",
"options": "Unit",
"reqd": 1
},
{
"fieldname": "section_break_11",
"fieldtype": "Section Break"
},
{
"fieldname": "section_break_13",
"fieldtype": "Section Break"
}
],
"is_submittable": 1,
"modified": "2021-01-18 12:35:14.883674",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Maintenance Ticket",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/maintenance_ticket/maintenance_ticket.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class MaintenanceTicket(Document):
pass

+ 10
- 0
muezzin/property_management/doctype/maintenance_ticket/test_maintenance_ticket.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestMaintenanceTicket(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/opening_phase/__init__.py 查看文件


+ 8
- 0
muezzin/property_management/doctype/opening_phase/opening_phase.js 查看文件

@@ -0,0 +1,8 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Opening Phase', {
// refresh: function(frm) {

// }
});

+ 43
- 0
muezzin/property_management/doctype/opening_phase/opening_phase.json 查看文件

@@ -0,0 +1,43 @@
{
"autoname": "field:phase_name",
"creation": "2021-04-12 01:36:49.755823",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"phase_name"
],
"fields": [
{
"fieldname": "phase_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Phase Name",
"reqd": 1,
"unique": 1
}
],
"modified": "2021-04-12 01:36:58.923784",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Opening Phase",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/opening_phase/opening_phase.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class OpeningPhase(Document):
pass

+ 10
- 0
muezzin/property_management/doctype/opening_phase/test_opening_phase.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestOpeningPhase(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/property/__init__.py 查看文件


+ 58
- 0
muezzin/property_management/doctype/property/property.js 查看文件

@@ -0,0 +1,58 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Property', {
refresh: function(frm) {
if (!frm.is_dirty()) {
create_custom_buttons(frm);
}
}
});

const create_custom_buttons = function(frm) {
frm.add_custom_button(__('Journal Entry'), () => {
let doc = frm.doc
frappe.run_serially([
() => frappe.new_doc('Journal Entry'),
() => {
cur_frm.doc.cost_center = doc.cost_center;
cur_frm.doc.property = doc.name;
cur_frm.refresh();
}
]);
}, __('Create'));

frm.add_custom_button(__('Sales Invoice'), () => {
let doc = frm.doc
frappe.run_serially([
() => frappe.new_doc('Sales Invoice'),
() => {
cur_frm.doc.cost_center = doc.cost_center;
cur_frm.doc.property = doc.name;
cur_frm.refresh();
}
]);
}, __('Create'));

frm.add_custom_button(__('Payment Entry'), () => {
let doc = frm.doc
frappe.run_serially([
() => frappe.new_doc('Payment Entry'),
() => {
cur_frm.doc.cost_center = doc.cost_center;
cur_frm.doc.property = doc.name;
if (doc.customer) {
cur_frm.doc.party_type = 'Customer';
cur_frm.doc.party = doc.customer;
cur_frm.doc.party_name = doc.customer_name;
}
if (doc.supplier) {
cur_frm.doc.party_type = 'Supplier';
cur_frm.doc.party = doc.supplier;
cur_frm.doc.party_name = doc.supplier_name;
}
cur_frm.refresh();
}
]);
}, __('Create'));
}

+ 499
- 0
muezzin/property_management/doctype/property/property.json 查看文件

@@ -0,0 +1,499 @@
{
"autoname": "field:property_name",
"creation": "2021-01-15 17:09:32.926540",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"customer",
"customer_name",
"column_break_2",
"supplier",
"supplier_name",
"basic_information_section",
"property_name",
"property_type",
"property_code_or_plate",
"property_number",
"property_management_commission",
"subject_to_vat",
"property_location",
"about_him",
"column_break_9",
"property_address",
"area_of_property",
"construction_year",
"status",
"governorate",
"city",
"contact",
"type_of_commission_for_property_management",
"calculated_as_a_percentage",
"accounting_dimensions_section",
"cost_center",
"property_data_section",
"property_owner",
"property_owner_company",
"property_owner_supplier",
"property_owner_customer",
"instrument_number",
"land__real_estate_number",
"bank",
"column_break_21",
"instrument_history",
"instrument_case",
"instrument_image_section",
"attach_instrument_image",
"property_evaluation_data_section",
"real_estate_evaluation",
"amount_of_evaluation",
"purchasing_price",
"column_break_30",
"date_of_evaluation",
"resident_agency",
"date_of_purchase",
"picture_of_property_section",
"attach_property_image",
"additional_data_section",
"number_of_elevators",
"national_address",
"number_of_positions",
"real_estate_facilities",
"electricity_account_number",
"cleanliness_for_common_entrance",
"column_break_43",
"number_of_floors",
"flat_construction",
"property_features",
"water_account_number",
"architecture_guard_data_section",
"building_guard_name",
"architecture_guard_identity",
"column_break_51",
"building_guards_mobile_number",
"adding_units_to_property_section",
"units",
"location_on_the_map_section",
"latitude",
"amended_from"
],
"fields": [
{
"fieldname": "basic_information_section",
"fieldtype": "Section Break",
"label": "Basic Information"
},
{
"fieldname": "property_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Property Name",
"no_copy": 1,
"reqd": 1,
"unique": 1
},
{
"fieldname": "property_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Property Type",
"options": "Property Type",
"reqd": 1
},
{
"fieldname": "governorate",
"fieldtype": "Link",
"label": "Governorate",
"options": "Territory"
},
{
"fieldname": "property_management_commission",
"fieldtype": "Data",
"label": "Property Management Commission"
},
{
"default": "0",
"fieldname": "subject_to_vat",
"fieldtype": "Check",
"label": "Subject to VAT"
},
{
"fieldname": "about_him",
"fieldtype": "Small Text",
"label": "About Him"
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"fieldname": "property_address",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Property Address",
"reqd": 1
},
{
"fieldname": "area_of_property",
"fieldtype": "Data",
"label": "Area of Property"
},
{
"fieldname": "construction_year",
"fieldtype": "Data",
"label": "Construction Year"
},
{
"fieldname": "city",
"fieldtype": "Link",
"label": "City",
"options": "Territory"
},
{
"fieldname": "type_of_commission_for_property_management",
"fieldtype": "Select",
"label": "Type of Commission for Property Management",
"options": "With Payments Made\nAnnual\nMid Term\nQuarterly"
},
{
"default": "0",
"fieldname": "calculated_as_a_percentage",
"fieldtype": "Check",
"label": "Calculated as a Percentage"
},
{
"fieldname": "property_data_section",
"fieldtype": "Section Break",
"label": "Property Data"
},
{
"fieldname": "instrument_number",
"fieldtype": "Data",
"label": "Instrument Number"
},
{
"fieldname": "land__real_estate_number",
"fieldtype": "Data",
"label": "Land / Real Estate Number"
},
{
"fieldname": "bank",
"fieldtype": "Link",
"label": "Bank",
"options": "Bank"
},
{
"fieldname": "column_break_21",
"fieldtype": "Column Break"
},
{
"fieldname": "instrument_history",
"fieldtype": "Date",
"label": "Instrument History"
},
{
"fieldname": "instrument_case",
"fieldtype": "Select",
"label": "Instrument Case",
"options": "Mortgaged\nNot Mortgaged"
},
{
"fieldname": "instrument_image_section",
"fieldtype": "Section Break",
"label": "Instrument Image"
},
{
"fieldname": "property_evaluation_data_section",
"fieldtype": "Section Break",
"label": "Property Evaluation Data"
},
{
"fieldname": "real_estate_evaluation",
"fieldtype": "Data",
"label": "Real Estate Evaluation"
},
{
"fieldname": "amount_of_evaluation",
"fieldtype": "Data",
"label": "Amount of Evaluation"
},
{
"fieldname": "purchasing_price",
"fieldtype": "Float",
"label": "Purchasing Price"
},
{
"fieldname": "column_break_30",
"fieldtype": "Column Break"
},
{
"fieldname": "date_of_evaluation",
"fieldtype": "Date",
"label": "Date of Evaluation"
},
{
"fieldname": "resident_agency",
"fieldtype": "Data",
"label": "Resident Agency"
},
{
"fieldname": "date_of_purchase",
"fieldtype": "Date",
"label": "Date of Purchase"
},
{
"fieldname": "picture_of_property_section",
"fieldtype": "Section Break",
"label": "Picture of Property"
},
{
"fieldname": "additional_data_section",
"fieldtype": "Section Break",
"label": "Additional Data"
},
{
"fieldname": "number_of_elevators",
"fieldtype": "Int",
"label": "Number of Elevators"
},
{
"fieldname": "national_address",
"fieldtype": "Data",
"label": "National Address"
},
{
"fieldname": "number_of_positions",
"fieldtype": "Int",
"label": "Number of Positions"
},
{
"fieldname": "real_estate_facilities",
"fieldtype": "Small Text",
"label": "Real Estate Facilities"
},
{
"fieldname": "electricity_account_number",
"fieldtype": "Data",
"label": "Electricity Account Number"
},
{
"default": "0",
"fieldname": "cleanliness_for_common_entrance",
"fieldtype": "Check",
"label": "Cleanliness for Common Entrance"
},
{
"fieldname": "column_break_43",
"fieldtype": "Column Break"
},
{
"fieldname": "number_of_floors",
"fieldtype": "Int",
"label": "Number of Floors"
},
{
"fieldname": "flat_construction",
"fieldtype": "Data",
"label": "Flat Construction"
},
{
"fieldname": "property_features",
"fieldtype": "Small Text",
"label": "Property Features"
},
{
"fieldname": "water_account_number",
"fieldtype": "Data",
"label": "Water Account Number"
},
{
"fieldname": "architecture_guard_data_section",
"fieldtype": "Section Break",
"label": "Architecture Guard Data"
},
{
"fieldname": "building_guard_name",
"fieldtype": "Data",
"label": "Building Guard Name"
},
{
"fieldname": "architecture_guard_identity",
"fieldtype": "Data",
"label": "Architecture Guard Identity"
},
{
"fieldname": "column_break_51",
"fieldtype": "Column Break"
},
{
"fieldname": "building_guards_mobile_number",
"fieldtype": "Data",
"label": "Building Guard's Mobile Number"
},
{
"fieldname": "adding_units_to_property_section",
"fieldtype": "Section Break",
"label": "Adding Units to Property"
},
{
"fieldname": "units",
"fieldtype": "Table",
"label": "Units",
"options": "Property Unit Detail"
},
{
"fieldname": "location_on_the_map_section",
"fieldtype": "Section Break",
"label": "Location on the map"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Property",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "attach_instrument_image",
"fieldtype": "Attach Image",
"label": "Image"
},
{
"fieldname": "attach_property_image",
"fieldtype": "Attach Image",
"label": "Image"
},
{
"fieldname": "contact",
"fieldtype": "Link",
"label": "Contact",
"options": "Contact"
},
{
"fieldname": "latitude",
"fieldtype": "Data",
"label": "Latitude"
},
{
"fieldname": "property_location",
"fieldtype": "Select",
"label": "Property Location",
"options": "Arkan Extention\nArkan Existing"
},
{
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"options": "Available\nRented\nSold"
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"no_copy": 1,
"options": "Customer",
"read_only": 1
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"fieldname": "supplier",
"fieldtype": "Link",
"label": "Supplier",
"no_copy": 1,
"options": "Supplier",
"read_only": 1
},
{
"fieldname": "property_code_or_plate",
"fieldtype": "Int",
"label": "Property Code / Plate"
},
{
"fieldname": "property_number",
"fieldtype": "Int",
"label": "Property Number"
},
{
"fieldname": "customer_name",
"fieldtype": "Data",
"label": "Customer Name",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "supplier_name",
"fieldtype": "Data",
"label": "Supplier Name",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "property_owner",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Property Owner",
"options": "\nCompany\nSupplier\nCustomer",
"reqd": 1
},
{
"depends_on": "eval:doc.property_owner=='Company'",
"fieldname": "property_owner_company",
"fieldtype": "Link",
"label": "Property Owner Company",
"options": "Company"
},
{
"depends_on": "eval:doc.property_owner=='Supplier'",
"fieldname": "property_owner_supplier",
"fieldtype": "Link",
"label": "Property Owner Supplier",
"options": "Supplier"
},
{
"depends_on": "eval:doc.property_owner=='Customer'",
"fieldname": "property_owner_customer",
"fieldtype": "Link",
"label": "Property Owner Customer",
"options": "Customer"
}
],
"modified": "2021-03-01 16:41:28.607988",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Property",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/property/property.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class Property(Document):
pass

+ 10
- 0
muezzin/property_management/doctype/property/test_property.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestProperty(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/property_contract/__init__.py 查看文件


muezzin/property_management/doctype/property_contract/property_contract → opy).py 查看文件

@@ -0,0 +1,545 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe import _
import datetime
# from datetime import datetime, timedelta
import dateutil
import calendar
from calendar import weekday, monthrange
from collections import Counter
from dateutil.relativedelta import relativedelta
from dateutil.rrule import rrule, MONTHLY, YEARLY
from frappe.model.document import Document
from frappe.utils import flt, date_diff, getdate, cint, nowdate,get_datetime


class PropertyContract(Document):
def validate(self):
if self.is_new():
self.add_payment()
# self.add_contract_annual_raise()

def before_submit(self):
"""
Validates if property already on rent between the from and to dates
"""

# Get Property Contaract if from date is between new from and new to date
filters = {
"rent_start_date": ["between", [self.rent_start_date, self.rent_end_date]],
"docstatus": 1,
"unit": self.unit,
"status": ["!=", "Hold"]
}
property_contract = frappe.get_list("Property Contract",
fields = ["name"],
filters = filters)

if len(property_contract)> 0:
frappe.throw(f"This property unit already contain a contract <a href='#Form/Property%20Contract/{property_contract[0].name}'>{property_contract[0].name}</a> that not ended yet.")


# Get Property Contaract if to date is between new from and new to date
filters = {
"rent_end_date": ["between", [self.rent_start_date, self.rent_end_date]],
"docstatus": 1,
"unit": self.unit,
"status": ["!=", "Hold"]
}
property_contract = frappe.get_list("Property Contract",
fields = ["name"],
filters = filters)

if len(property_contract)> 0:
frappe.throw(f"This property unit already contain a contract <a href='#Form/Property%20Contract/{property_contract[0].name}'>{property_contract[0].name}</a> that not ended yet.")


# Get Property Contaract if from date is after new from date and to date is before new to date
filters = {
"rent_start_date": ["<=", self.rent_start_date],
"rent_end_date": [">=", self.rent_end_date],
"docstatus": 1,
"unit": self.unit,
"status": ["!=", "Hold"]
}
property_contract = frappe.get_list("Property Contract",
fields = ["name"],
filters = filters)

if len(property_contract)> 0:
frappe.throw(f"This property unit already contain a contract <a href='#Form/Property%20Contract/{property_contract[0].name}'>{property_contract[0].name}</a> that not ended yet.")

diff_days = date_diff(self.rent_end_date,getdate())
if flt(diff_days) > 0:
frappe.db.set_value("Unit", self.unit, "status", "Leased")

def add_contract_annual_raise(self):
self.contract_annual_raise = []
start_date = datetime.datetime.strptime(self.rent_start_date, "%Y-%m-%d")
duration = self.duration - 1 if self.duration > 1 else self.duration
month = dateutil.relativedelta.relativedelta(months=duration)
end_date = start_date + month

for row in rrule(YEARLY, dtstart=start_date, until=end_date):
self.append('contract_annual_raise', {
'date': row.date(),
'amount': 0,
})

def add_payment(self):

self.payments = []
start_date = datetime.datetime.strptime(self.rent_start_date, "%Y-%m-%d")
duration = self.duration - 1 if self.duration > 1 else self.duration
month = dateutil.relativedelta.relativedelta(months=duration)
end_date = start_date + month
if not self.annual_raise_start_date:
duration_to_add = 13 - start_date.month
months = dateutil.relativedelta.relativedelta(months = duration_to_add)
self.annual_raise_start_date = start_date + months
self.annual_raise_start_date = self.annual_raise_start_date.strftime('%Y-%m-%d')

annual_raise_start_date = datetime.datetime.strptime(str(self.annual_raise_start_date), "%Y-%m-%d")
service_charge_start_date = datetime.datetime.strptime(str(self.service_charge_start_date), "%Y-%m-%d")
frequency = 0
if self.type == 'Customized c':
if self.payments_scheduling:
frequency = self.payments_scheduling
else:
if self.type == 'Monthly':
frequency = 1
if self.type == 'Quarterly':
frequency = 4
if self.type == 'Half Yearly':
frequency = 6
if self.type == 'Annually':
frequency = 12

annual_raise_amount = self.annual_raise_amount
rent = (self.total_rent_per_month if not self.split_indoor_from_outdoor_rent else self.indoor_rent_month)
year = annual_raise_start_date.year
service_year = start_date.year
service_charge =0.0

if self.split_indoor_from_outdoor_service:
service_charge = self.service_charge_per_month or 0
else:
service_charge = self.total_services_rent or 0

flag=1

service_charge_frequency = 1
if self.service_charges_type == 'Annually':
service_charge_frequency = 12
if flt(service_charge) > 0 :
for row in rrule(MONTHLY, interval=service_charge_frequency, dtstart=start_date, until=end_date):
if (self.service_raise_type == "Annual Regular"):
if row.month == service_charge_start_date.month and row.year >= service_charge_start_date.year:
# if row.year >service_year:
service_year=row.year
if self.annual_service_charges == "Percentage":
service_charge += service_charge / 100 * int((self.amount if self.amount else "0"))
else:
service_charge += int(self.amount)

if (self.service_raise_type == "Annual Irregular"):
ourdoor_service_dict = {}
for sub_row in self.service_raise_details:
date = datetime.datetime.strptime(sub_row.date, "%Y-%m-%d")
date = date.strftime("%d-%m-%Y")
ourdoor_service_dict[date] = f'{sub_row.annual_raise_type}_{sub_row.annual_raise_amount}'

month = '{:02d}'.format(row.month)
day = '{:02d}'.format(row.day)

fieldname = f'{day}-{month}-{row.year}'

if fieldname in ourdoor_service_dict and ourdoor_service_dict[fieldname]:
splitted_field = ourdoor_service_dict[fieldname].split("_")
annual_raise_type = splitted_field[0]
annual_raise_amount = splitted_field[1]
# print("===================>" + str(annual_raise_amount))
if annual_raise_type == "Percentage":
service_charge += service_charge / 100 * int(annual_raise_amount)
else:
service_charge += int(annual_raise_amount)

self.append('payments', {
'date': row.date(),
'amount': service_charge or 0,
'reason':'Services Charges'+("" if not self.split_indoor_from_outdoor_service else " - InDoor")
})

if self.split_indoor_from_outdoor_service:
service_charge = self.outdoor_service_charge_per_month
year = service_charge_start_date.year
service_year = start_date.year
if service_charge > 0:
for row in rrule(MONTHLY, interval=service_charge_frequency, dtstart=start_date, until=end_date):
if (self.service_raise_type == "Annual Regular"):
if row.month == service_charge_start_date.month and row.year >= service_charge_start_date.year:
# if row.year > service_year:
service_year = row.year
if self.annual_service_charges == "Percentage":
service_charge += service_charge / 100 * int(self.amount)
else:
service_charge += int(self.amount)

if (self.service_raise_type == "Annual Irregular"):
ourdoor_service_dict = {}
for sub_row in self.service_raise_details:
date = datetime.datetime.strptime(sub_row.date, "%Y-%m-%d")
date = date.strftime("%d-%m-%Y")
ourdoor_service_dict[date] = f'{sub_row.annual_raise_type}_{sub_row.annual_raise_amount}'

month = '{:02d}'.format(row.month)
day = '{:02d}'.format(row.day)

fieldname = f'{day}-{month}-{row.year}'

if fieldname in ourdoor_service_dict and ourdoor_service_dict[fieldname]:
splitted_field = ourdoor_service_dict[fieldname].split("_")
annual_raise_type = splitted_field[0]
annual_raise_amount = splitted_field[1]
# print("===================>" + str(annual_raise_amount))
if annual_raise_type == "Percentage":
service_charge += service_charge / 100 * int(annual_raise_amount)
else:
service_charge += int(annual_raise_amount)

self.append('payments', {
'date': row.date(),
'amount': service_charge or 0,
'reason': 'Services Charges'+" - " +"OutDoor"
})

for row in rrule(MONTHLY, interval=frequency, dtstart=start_date, until=end_date):
if(self.annual_raise == "Annual Regular"):
if row.year >= annual_raise_start_date.year:
#if row.month >= annual_raise_start_date.month or row.year > annual_raise_start_date.year:
if row.month == annual_raise_start_date.month and row.year >= annual_raise_start_date.year:
if row.year > year :
year=row.year
flag=1

if flag == 1:
if self.annual_raise_type == "Percentage":
rent += rent / 100 * int(annual_raise_amount)
else:
rent += int(annual_raise_amount)
flag = 0

if(self.annual_raise == "Annual Irregular"):
contract_dict = {}
for sub_row in self.contract_annual_raise:
date = datetime.datetime.strptime(sub_row.date, "%Y-%m-%d")
date = date.strftime("%d-%m-%Y")
contract_dict[date] = f'{sub_row.annual_raise_type}_{sub_row.annual_raise_amount}'


month = '{:02d}'.format(row.month)
day = '{:02d}'.format(row.day)

fieldname = f'{day}-{month}-{row.year}'

if fieldname in contract_dict and contract_dict[fieldname]:
splitted_field = contract_dict[fieldname].split("_")
annual_raise_type = splitted_field[0]
annual_raise_amount = splitted_field[1]
# print("===================>" + str(annual_raise_amount))
if annual_raise_type == "Percentage":
rent += rent / 100 * int(annual_raise_amount)
else:
rent += int(annual_raise_amount)

self.append('payments', {
'date': row.date(),
'amount': rent or 0,
'reason':'Rent'+(' - indoor' if self.split_indoor_from_outdoor_rent else '')
})

if self.split_indoor_from_outdoor_rent:
flag = 1
rent = self.outdoor_rent_month
if rent > 0:
for row in rrule(MONTHLY, interval=frequency, dtstart=start_date, until=end_date):
if (self.annual_raise == "Annual Regular"):
if row.year >= annual_raise_start_date.year:
#if row.month >= annual_raise_start_date.month or row.year > annual_raise_start_date.year:
if row.month == annual_raise_start_date.month and row.year >= annual_raise_start_date.year:
if row.year > year:
year = row.year
flag = 1

if flag == 1:
if self.annual_raise_type == "Percentage":
rent += rent / 100 * int(annual_raise_amount)
else:
rent += int(annual_raise_amount)
flag = 0

if (self.annual_raise == "Annual Irregular"):
contract_dict = {}
for sub_row in self.contract_annual_raise:
date = datetime.datetime.strptime(sub_row.date, "%Y-%m-%d")
date = date.strftime("%d-%m-%Y")
contract_dict[date] = f'{sub_row.annual_raise_type}_{sub_row.annual_raise_amount}'

month = '{:02d}'.format(row.month)
day = '{:02d}'.format(row.day)

fieldname = f'{day}-{month}-{row.year}'

if fieldname in contract_dict and contract_dict[fieldname]:
splitted_field = contract_dict[fieldname].split("_")
annual_raise_type = splitted_field[0]
annual_raise_amount = splitted_field[1]

if annual_raise_type == "Percentage":
rent += rent / 100 * int(annual_raise_amount)
else:
rent += int(annual_raise_amount)

self.append('payments', {
'date': row.date(),
'amount': rent or 0,
'reason': 'Rent' + ' - outdoor'
})

if not self.is_new():
self.save()

def on_cancel(self):
frappe.db.set_value("Unit", self.unit, "status", "Vacant")

def create_row_invoice(self,rowid):
create_sales_invoice(self.name,rowid)

def get_brand_names(self):
list_items = []
brands = frappe.get_all("Tenant Brand Name", filters={"parent": self.renter}, fields=['tenant_brand_name'], order_by="tenant_brand_name")
if brands:
for item in brands:
list_items.append(item["tenant_brand_name"])

return list_items

def update_duration(self):
duration = self.get_dates_diff(self.rent_start_date,self.rent_end_date)
# frappe.msgprint(str(duration))

duration_months = (cint(duration["years"]) * 12) + cint(duration["months"]) + (1 if cint(duration["days"]) > 0 or getdate(self.rent_end_date).day == 1 else 0)
# frappe.msgprint(str(duration_months))
self.duration = duration_months

return duration_months

def get_dates_diff(self,a, b):
a = getdate(a)
b = getdate(b)
diff_dict = {}
# a = datetime(a.year, a.month, a.day)
# b = datetime(b.year, b.month, b.day)
delta = dateutil.relativedelta.relativedelta(b, a)
diff_dict["years"] = delta.years
diff_dict["months"] = delta.months
diff_dict["days"] = delta.days

return diff_dict

def create_sales_invoice(docname="",rowid=""):
print("Starting Creation..........")
condiction = ""
if docname and rowid:
row_doc = frappe.get_doc("Contract Payments",rowid)
condiction = " and C.parent = '%s' " % docname
# condiction += " and C.`name` = '%s' " % rowid
condiction += " and C.date = '%s' " % row_doc.date
invoices = frappe.db.sql("""
select *,P.`name` contract_name,C.date invoice_date,C.amount row_amount
from `tabProperty Contract` P
inner JOIN
`tabContract Payments` C
on P.`name` = C.parent
-- and CURRENT_DATE() >= C.date
and P.rent_item is not NULL
and P.service_item is not NULL
-- and P.docstatus = 1
and C.invoice is Null
and C.amount > 0
{0}
order by date
""".format(condiction),as_dict=True,debug=True)

if invoices:
# for invoice in invoices:
# s_name = create_sales_invoices(invoice)
# print(str(s_name.name)+" Invoice Created.")
s_name = create_sales_invoices(invoices)
print("Finished.")

def create_sales_invoices(invoices):
# frappe.throw(str(args))
default_company = frappe.db.get_default("Company")
si = frappe.new_doc("Sales Invoice")

if isinstance(invoices,list):
args = frappe._dict(invoices[0])
else:
args = frappe._dict(invoices)

invoice_date = args.date or nowdate()
si.posting_date = invoice_date

debit_to = frappe.db.get_value("Company", default_company, 'default_receivable_account')
if debit_to:
si.debit_to = debit_to # Default Receivable Account
else:
frappe.throw(_("Please set Default Receivable Account in Company " + default_company))

si.set_posting_time = 1
si.postint_date = args.invoice_date
si.due_date = args.invoice_date

si.customer = args.renter
si.tax_id = frappe.db.get_value("Customer",args.renter,"tax_id")
si.debit_to = debit_to

print(str(si.postint_date)+"================================================="+str(si.due_date))

#Accounting Dimensions
si.property = args.property_name
si.unit = args.unit
si.property_contract = args.contract_name
invoice_curerncy = frappe.db.get_value('Customer', args.renter, "default_currency") or frappe.get_cached_value('Company', default_company, "default_currency")

si.currency=invoice_curerncy
conversion_rate = 1
if invoice_curerncy !="EGP":
conversion_rate = get_conversion_rate(invoice_curerncy,invoice_date)
if conversion_rate == 0:
message = _("Please Insert Currency Exchange for day {0}".format(invoice_date))
frappe.throw(frappe.bold(message))

si.conversion_rate = conversion_rate
tax_list = []
old_template = ""
for item in invoices:
default_income_account = None
item_income_account = frappe.db.get_value("Item Default", {"parent":(item.rent_item if item.reason =="Rent" else item.service_item)}, 'income_account')
company_default_income_account = frappe.db.get_value("Company", default_company, 'default_income_account')

if item_income_account:
default_income_account = item_income_account
elif company_default_income_account:
default_income_account = company_default_income_account
else:
frappe.throw(_("Please set Default Income Account in Company " + default_company))

cost_center = frappe.db.get_value("Company", default_company, 'cost_center')
if not cost_center:
frappe.throw(_("Please set Default Cost Center in Company " + default_company))


tax_template_name = ""
if item.reason =="Services Charges" and item.service_sales_taxes_and_charges_template:
tax_template_name = item.service_sales_taxes_and_charges_template
elif item.reason =="Rent" and item.rent_sales_taxes_and_charges_template:
tax_template_name = item.rent_sales_taxes_and_charges_template

si.append("items", {
"item_code": (item.rent_item if item.reason =="Rent" else item.service_item) ,
"item_name": (item.rent_item_name if item.reason =="Rent" else item.service_item),
"qty": 1,
"item_tax_template": (tax_template_name if tax_template_name else "") ,
"rate": item.row_amount ,
"uom": item.uom or "Unit",
"stock_uom": item.uom or "Unit",
"income_account": default_income_account,# Default Income Account
"cost_center": cost_center ,
"conversion_factor": 1,
"description" : item.reason+" for contract "+ str(item.contract_name)
})


if tax_template_name:
if old_template=="":
old_template = tax_template_name

tax_data = frappe.db.sql("""
select *
from `tabItem Tax Template Detail`
where parent='{tax_template_name}'
""".format(tax_template_name=tax_template_name),as_dict=True,debug=False)

if tax_data:
tax_data = tax_data[0]
if old_template == tax_template_name:
tax_list = []

tax_list.append({
"charge_type":"On Net Total",
"account_head":tax_data["tax_type"],
"description":tax_data["parent"],
"rate": 0 , #tax_data["tax_rate"],
"tax_amount":flt(tax_data["tax_rate"]) * flt(item.row_amount),
"total":(flt(tax_data["tax_rate"]) * flt(item.row_amount)) + flt(item.row_amount),
"base_tax_amount":flt(tax_data["tax_rate"]) * flt(item.row_amount),
"base_total":(flt(tax_data["tax_rate"]) * flt(item.row_amount)) + flt(item.row_amount)
})

for item in tax_list:
si.append("taxes",item)

Sinv_ID = None
try:
Sinv_ID = si.insert(ignore_mandatory = True)
for row in invoices:
frappe.db.sql("""update `tabContract Payments` set invoice = '{0}' where `name` = '{1}' """.format(Sinv_ID.name,row.name),debug=True)

return Sinv_ID
except Exception as e:
print("Error......"+str(e))
frappe.throw(_(e))

def get_conversion_rate(invoice_curerncy,invoice_date):
conversion_rate = 0
exchange_rate = frappe.db.sql("""
select exchange_rate
from `tabCurrency Exchange`
where from_currency ='{invoice_curerncy}'
and to_currency='EGP'
and date ='{invoice_date}'
""".format(invoice_curerncy=invoice_curerncy,invoice_date=invoice_date),as_dict=True)
if exchange_rate:
conversion_rate = exchange_rate[0]["exchange_rate"]

return conversion_rate



def update_unit_status():
units = frappe.db.get_list("Property Contract",filters={"docstatus":1,"rent_end_date":getdate()},fields=["unit"])
if units:
for unit in units:
frappe.db.set_value("Unit", unit["unit"], "status", "Vacant")



def update_unit_status_basd_on_reservation():
print("==========================starting============================")
units = frappe.db.get_list("Reservations",filters={"docstatus":1,"reservation_end":["<",get_datetime()]},fields=["unit"])
if units:
for unit in units:
frappe.db.set_value("Unit", unit["unit"], "status", "Vacant")
print("==========================Finished============================")

+ 351
- 0
muezzin/property_management/doctype/property_contract/property_contract.js 查看文件

@@ -0,0 +1,351 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Property Contract', {
onload: function (frm) {
get_brand_names(frm)
},
generate_payment_scheduale: function (frm) {
frm.doc.payment = []
frappe.call({
method: 'add_payment',
doc: frm.doc,
callback: function (res) {
frappe.show_alert('Generated Successfully')

cur_frm.reload_doc()
}
})
},
renter: function (frm) {
get_brand_names(frm)
},
refresh: function (frm) {
get_brand_names(frm)
frm.set_query('property_name', () => {
return {
filters: {
property_type: frm.doc.property_type
}
}
})
frm.set_query('unit', () => {
return {
filters: {
unit_type: frm.doc.unit_type,
property: frm.doc.property_name
}
}
})
frm.set_query('rent_item', () => {
return {
filters: {
is_sales_item: 1
}
}
})
frm.set_query('service_item', () => {
return {
filters: {
is_sales_item: 1
}
}
})
frm.clear_custom_buttons()

if (frm.doc.docstatus == 0 && !frm.is_new()) {
frm.add_custom_button(__("Generate Payment Scheduale"), function () {
frm.trigger('generate_payment_scheduale');
});
}

if (frm.doc.docstatus == 1 && frm.doc.status != "Hold") {
frm.add_custom_button("Hold Contract", () => {
frappe.call({
method: "muezzin.api.property_contract.change_contract_status",
args: {
doc: frm.doc,
status: "Hold"
},
callback: () => {
frm.reload_doc();
}
})
})
}
if (frm.doc.docstatus == 1 && frm.doc.status == "Hold") {
frm.add_custom_button("Release Contract", () => {
frappe.call({
method: "muezzin.api.property_contract.change_contract_status",
args: {
doc: frm.doc,
status: "Active"
},
callback: () => {
frm.reload_doc();
}
})
})
}
},

rent_start_date: function (frm) {
update_duration(frm)
// if (frm.doc.docstatus == 0 && !frm.is_new()) {
// frm.trigger('generate_payment_scheduale');
// }
},
rent_end_date: function (frm) {
update_duration(frm)
// if (frm.doc.docstatus == 0 && !frm.is_new()) {
// frm.trigger('generate_payment_scheduale');
// }
},

// annual_raise_start_date: function (frm) {
// if (frm.doc.docstatus == 0 && !frm.is_new()) {
// frm.trigger('generate_payment_scheduale');
// }
// },
// service_charge_start_date: function (frm) {
// if (frm.doc.docstatus == 0 && !frm.is_new()) {
// frm.trigger('generate_payment_scheduale');
// }
// },

property_type: function (frm) {
frm.doc.property_name = null;
frm.refresh_field('property_name');
},

unit_type: function (frm) {
frm.doc.property_name = null;
frm.refresh_field('unit');
},

// duration: function(frm) {
// update_payment(frm);
// update_annual_raise_amount(frm);
// },

// type: function(frm) {
// update_payment(frm);
// },

// payments_scheduling: function(frm) {
// update_payment(frm);
// },

unit: function (frm) {
update_total_unit_area(frm);
},
service_indoor_area: function (frm) {
update_service_charge_per_month(frm);
},
service_charge_sqm: function (frm) {
update_service_charge_per_month(frm);
},
outdoor_service_charge_per_month: function (frm) {

update_total_services_rent(frm)
update_total_rent(frm);

// update_total_rent_month(frm);
update_total_services_rent(frm)
update_outdoor_service_charge_sqm(frm);
},

service_outdoor_area: function (frm) {
// update_outdoor_service_charge_sqm(frm);
},
outdoor_service_charge_sqm: function (frm) {
update_outdoor_service_charge_sqm(frm);
},
indoor_area: function (frm) {
update_indoor_rent_month(frm);
},

rent_per_sqm: function (frm) {
update_indoor_rent_month(frm);
},

indoor_rent_month: function (frm) {
update_total_rent_month(frm);
},

// outdoor_service_charge_per_month: function(frm) {
// update_total_rent_month(frm);
// },
outdoor_area: function (frm) {
update_outdoor_rent_month(frm);
},

outdoor_rent_sqm: function (frm) {
update_outdoor_rent_month(frm);
},

outdoor_rent_month: function (frm) {
update_total_rent_month(frm);
},

total_rent_per_month: function (frm) {
update_total_rent(frm);
},

service_charge_per_month: function (frm) {
update_total_services_rent(frm)
update_total_rent(frm);
},

total_rent: function (frm) {
if (frm.doc.total_rent) {
frm.set_value('annualized_rent', frm.doc.total_rent * 12);
}
}
});
frappe.ui.form.on('Contract Payments', {
create_invoice: function (frm, cdt, cdn) {
var row = locals[cdt][cdn];
if (!row.invoice) {
frm.call({
method: 'create_row_invoice',
doc: frm.doc,
args: {rowid: cdn},
callback: function (res) {
frm.reload_doc();
}
})
} else {
frappe.throw(__('Invoice Already Created..'))
}
}
})

const update_payment = function (frm) {
if (!frm.doc.rent_start_date) {
frm.doc.duration = null;
frm.refresh_field('duration');
frappe.msgprint('Enter Rent Start Date.')
} else {
if (frm.doc.type && frm.doc.duration) {
frm.call('add_payment');
frm.refresh_field('payments');
} else {
frm.doc.payments = [];
frm.refresh_field('payments');
}
}
}

const update_annual_raise_amount = function (frm) {
if (!frm.doc.rent_start_date) {
frm.doc.duration = null;
frm.refresh_field('duration');
frappe.msgprint('Enter Rent Start Date.')
} else {
if (frm.doc.annual_raise === 'Annual Irregular' && frm.doc.duration) {
frm.call('add_contract_annual_raise');
frm.refresh_field('contract_annual_raise');
} else {
frm.doc.contract_annual_raise = [];
frm.refresh_field('contract_annual_raise');
}
}
}

// const update_service_charge = function(frm) {
// if(frm.doc.gla && frm.doc.service_charge_sqm) {
// frm.set_value('service_charge_per_month', frm.doc.gla * frm.doc.service_charge_sqm)
// }
// }

const update_total_rent_month = function (frm) {
if (frm.doc.indoor_rent_month >= 0 && frm.doc.outdoor_rent_month >= 0) {
frm.set_value('total_rent_per_month', flt(frm.doc.indoor_rent_month) + flt(frm.doc.outdoor_rent_month) + flt(frm.doc.outdoor_service_charge_per_month));
}
}

const update_total_rent = function (frm) {
if (frm.doc.total_rent_per_month >= 0 && frm.doc.service_charge_per_month >= 0) {
frm.set_value('total_rent', flt(frm.doc.total_rent_per_month) + flt(frm.doc.service_charge_per_month) + flt(frm.doc.outdoor_service_charge_per_month));
}
}

const update_outdoor_rent_month = function (frm) {
// if (frm.doc.outdoor_area && frm.doc.outdoor_rent_sqm) {
frm.set_value('outdoor_rent_month', frm.doc.outdoor_area * frm.doc.outdoor_rent_sqm);
// }
}

const update_indoor_rent_month = function (frm) {
if (frm.doc.indoor_area && frm.doc.rent_per_sqm) {
frm.set_value('indoor_rent_month', frm.doc.indoor_area * frm.doc.rent_per_sqm);
}
}
const update_total_unit_area = function (frm) {
// if (frm.doc.gla && frm.doc.service_charge_sqm) {
frm.set_value('gla', frm.doc.outdoor_area + frm.doc.indoor_area);
refresh_field('gla')
// }
}
const update_service_charge_per_month = function (frm) {
// if (frm.doc.gla && frm.doc.service_charge_sqm) {
frm.set_value('service_charge_per_month', frm.doc.service_indoor_area * frm.doc.service_charge_sqm);
// }
}
const update_total_services_rent = function (frm) {
frm.set_value('total_services_rent', flt(frm.doc.outdoor_service_charge_per_month) + flt(frm.doc.service_charge_per_month));
}
const update_outdoor_service_charge_sqm = function (frm) {
// if (frm.doc.outdoor_service_charge_sqm && frm.doc.service_outdoor_area) {
// frm.set_value('outdoor_service_charge_sqm',0)
// frm.set_value('outdoor_rent_sqm',0)
frm.set_value('outdoor_service_charge_per_month', frm.doc.outdoor_service_charge_sqm * frm.doc.service_outdoor_area);
// refresh_field('outdoor_rent_sqm')
// refresh_field('outdoor_service_charge_per_month')
// }
}
const update_duration = function (frm) {
if (frm.doc.rent_start_date && frm.doc.rent_end_date) {
// let duration = get_time_diff(frm.doc.rent_end_date, frm.doc.rent_start_date)
// frm.set_value('duration', duration)

frappe.call({
method:'update_duration',
doc:frm.doc,
callback:function(r){
if(r.message){
frm.set_value('duration', r.message)
refresh_field('duration')
}
}
})
}
}

const get_time_diff = (end, start) => {
const end_datetime = new Date(end)
const start_datetime = new Date(start)
const time_diff = end_datetime - start_datetime
const months = time_diff / 2592000000
return Math.round(months)
}


let get_brand_names = function (frm) {
if (frm.doc.renter) {
frappe.call({
method: "muezzin.property_management.doctype.property_contract.property_contract.get_brand_names",
args: {
renter:frm.doc.renter
},
callback: function (data) {
if (data.message) {
let options = data.message;
let options_new = options.join("\n");
frm.set_df_property('brand_name', 'options', options_new);
}
}
})
}
}

+ 745
- 0
muezzin/property_management/doctype/property_contract/property_contract.json 查看文件

@@ -0,0 +1,745 @@
{
"autoname": "field:lease",
"creation": "2021-02-02 11:52:23.747179",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"main_data_section",
"lease",
"status",
"date_type",
"opening_date",
"rent_start_date",
"renter",
"brand_name",
"tenant_name_currency",
"is_vat_applied",
"column_break_7",
"writing_date",
"script",
"rent_end_date",
"migration_migrate_to_accounts",
"grace_period_day",
"revenue_sharing",
"insurance_policy",
"choose_units_section",
"property_type",
"unit_type",
"column_break_14",
"property_name",
"unit",
"items_section",
"rent_item",
"rent_item_name",
"rent_sales_taxes_and_charges_template",
"column_break_22",
"service_item",
"service_item_name",
"service_sales_taxes_and_charges_template",
"rent_details_section",
"indoor_area",
"rent_per_sqm",
"indoor_rent_month",
"column_break_24",
"service_indoor_area",
"service_charge_sqm",
"service_charge_per_month",
"section_break_28",
"outdoor_area",
"outdoor_rent_sqm",
"outdoor_rent_month",
"column_break_32",
"service_outdoor_area",
"outdoor_service_charge_sqm",
"outdoor_service_charge_per_month",
"totals_section",
"total_rent_per_month",
"total_rent",
"annualized_rent",
"column_break_25",
"gla",
"total_services_rent",
"lease_sent",
"lease_signed",
"split_indoor_from_outdoor_rent",
"split_indoor_from_outdoor_service",
"payments_scheduling_section",
"duration",
"payments_scheduling",
"annual_raise_start_date",
"annual_raise_type",
"column_break_19",
"type",
"annual_raise",
"annual_raise_amount",
"section_break_50",
"contract_annual_raise",
"section_break_23",
"service_raise_type",
"service_raise_details",
"annual_service_charges",
"amount",
"column_break_48",
"service_charges_type",
"service_charge_start_date",
"section_break_25",
"payments",
"expenses_section",
"maintenance_services_payment",
"water_payment",
"clining",
"owner_management",
"electricity_payment",
"lease_payment",
"column_break_29",
"ms_type",
"w_type",
"c_type",
"om_type",
"e_type",
"insurance_payment",
"column_break_36",
"ms_service",
"w_service",
"c_service",
"om_service",
"e_service",
"marketing_fees",
"amended_from"
],
"fields": [
{
"fieldname": "main_data_section",
"fieldtype": "Section Break",
"label": "Main Data"
},
{
"fieldname": "lease",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Lease",
"reqd": 1,
"unique": 1
},
{
"fieldname": "date_type",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Date Type",
"options": "Hijri\nGregorian",
"reqd": 1
},
{
"fieldname": "rent_start_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Rent start date",
"reqd": 1
},
{
"fieldname": "renter",
"fieldtype": "Link",
"label": "Legal Tenant Name",
"options": "Customer"
},
{
"default": "0",
"fieldname": "is_vat_applied",
"fieldtype": "Check",
"label": "Is Vat Applied"
},
{
"fieldname": "column_break_7",
"fieldtype": "Column Break"
},
{
"fieldname": "writing_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Writing Date",
"reqd": 1
},
{
"default": "0",
"fieldname": "migration_migrate_to_accounts",
"fieldtype": "Check",
"label": "Migration (Migrate to Accounts)"
},
{
"fieldname": "script",
"fieldtype": "Link",
"label": "Script",
"options": "Script"
},
{
"fieldname": "grace_period_day",
"fieldtype": "Data",
"label": "Grace period (day)"
},
{
"fieldname": "choose_units_section",
"fieldtype": "Section Break",
"label": "Choose Units"
},
{
"fieldname": "property_type",
"fieldtype": "Link",
"label": "Property Type",
"options": "Property Type"
},
{
"fieldname": "column_break_14",
"fieldtype": "Column Break"
},
{
"fieldname": "property_name",
"fieldtype": "Link",
"label": "Property Name",
"options": "Property",
"reqd": 1
},
{
"fieldname": "payments_scheduling_section",
"fieldtype": "Section Break",
"label": "Payments Scheduling"
},
{
"fieldname": "duration",
"fieldtype": "Int",
"label": "Duration",
"read_only": 1
},
{
"fieldname": "annual_raise",
"fieldtype": "Select",
"label": "Annual raise",
"options": "\nAnnual Regular\nAnnual Irregular"
},
{
"depends_on": "eval:doc.service_raise_type=='Annual Regular'",
"fieldname": "column_break_19",
"fieldtype": "Column Break"
},
{
"fieldname": "type",
"fieldtype": "Select",
"label": "Type",
"options": "Monthly\nQuarterly\nHalf Yearly\nAnnually\nCustomized c"
},
{
"depends_on": "eval:doc.type=='Customized c'",
"description": "Ex: 3, means new payment every 3 months among lease duration",
"fieldname": "payments_scheduling",
"fieldtype": "Int",
"label": "Payments Scheduling"
},
{
"fieldname": "expenses_section",
"fieldtype": "Section Break",
"label": "Expenses"
},
{
"fieldname": "lease_payment",
"fieldtype": "Currency",
"label": "Lease Payment",
"options": "tenant_name_currency"
},
{
"fieldname": "maintenance_services_payment",
"fieldtype": "Currency",
"label": "Maintenance Services Payment",
"options": "tenant_name_currency"
},
{
"fieldname": "water_payment",
"fieldtype": "Currency",
"label": "Water Payment",
"options": "tenant_name_currency"
},
{
"fieldname": "clining",
"fieldtype": "Currency",
"label": "Clining",
"options": "tenant_name_currency"
},
{
"fieldname": "owner_management",
"fieldtype": "Currency",
"label": "Owner Management",
"options": "tenant_name_currency"
},
{
"fieldname": "electricity_payment",
"fieldtype": "Currency",
"label": "Electricity Payment",
"options": "tenant_name_currency"
},
{
"fieldname": "column_break_29",
"fieldtype": "Column Break"
},
{
"fieldname": "insurance_payment",
"fieldtype": "Currency",
"label": "Insurance Payment",
"options": "Currency"
},
{
"fieldname": "ms_type",
"fieldtype": "Select",
"label": "Type",
"options": "Annually\nWith Payments\nMonthly\nJust Once"
},
{
"fieldname": "w_type",
"fieldtype": "Select",
"label": "Type",
"options": "Annually\nWith Payments\nMonthly\nJust Once"
},
{
"fieldname": "c_type",
"fieldtype": "Select",
"label": "Type",
"options": "Annually\nWith Payments\nMonthly\nJust Once"
},
{
"fieldname": "om_type",
"fieldtype": "Select",
"label": "Type",
"options": "Annually\nWith Payments\nMonthly\nJust Once"
},
{
"fieldname": "e_type",
"fieldtype": "Select",
"label": "Type",
"options": "Annually\nWith Payments\nMonthly\nJust Once"
},
{
"fieldname": "column_break_36",
"fieldtype": "Column Break"
},
{
"fieldname": "ms_service",
"fieldtype": "Select",
"label": "Service Provider",
"options": "\nSarabi\nMaqawal\nMisry"
},
{
"fieldname": "w_service",
"fieldtype": "Select",
"label": "Service Provider",
"options": "\nSarabi\nMaqawal\nMisry"
},
{
"fieldname": "c_service",
"fieldtype": "Select",
"label": "Service Provider",
"options": "\nSarabi\nMaqawal\nMisry"
},
{
"fieldname": "om_service",
"fieldtype": "Select",
"label": "Service Provider",
"options": "\nSarabi\nMaqawal\nMisry"
},
{
"fieldname": "e_service",
"fieldtype": "Select",
"label": "Service Provider",
"options": "\nSarabi\nMaqawal\nMisry"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Property Contract",
"print_hide": 1,
"read_only": 1
},
{
"depends_on": "eval:doc.annual_raise=='Annual Regular';",
"fieldname": "annual_raise_amount",
"fieldtype": "Data",
"label": "Annual Raise Amount"
},
{
"collapsible": 1,
"fieldname": "section_break_23",
"fieldtype": "Section Break",
"label": "SERVICE CHARGES"
},
{
"fieldname": "contract_annual_raise",
"fieldtype": "Table",
"label": "Annual Raise Details",
"options": "Contract Annual Raise"
},
{
"depends_on": "eval:doc.duration>0;",
"fieldname": "section_break_25",
"fieldtype": "Section Break"
},
{
"allow_on_submit": 1,
"fieldname": "payments",
"fieldtype": "Table",
"label": "Payments",
"options": "Contract Payments"
},
{
"fieldname": "rent_details_section",
"fieldtype": "Section Break",
"label": "Rent Details"
},
{
"fieldname": "rent_per_sqm",
"fieldtype": "Currency",
"label": "Rent (Per sqm)",
"options": "tenant_name_currency"
},
{
"fieldname": "outdoor_rent_sqm",
"fieldtype": "Currency",
"label": "Outdoor Rent (sqm)",
"options": "tenant_name_currency"
},
{
"fieldname": "total_rent_per_month",
"fieldtype": "Currency",
"label": "Total Rent per Month",
"options": "tenant_name_currency",
"read_only": 1
},
{
"fieldname": "service_charge_sqm",
"fieldtype": "Currency",
"label": "Service Charge sqm",
"options": "tenant_name_currency"
},
{
"fieldname": "service_charge_per_month",
"fieldtype": "Currency",
"label": "Service Charge per Month",
"options": "tenant_name_currency"
},
{
"fieldname": "total_rent",
"fieldtype": "Currency",
"label": "Total Rent",
"options": "tenant_name_currency",
"read_only": 1
},
{
"fieldname": "annualized_rent",
"fieldtype": "Currency",
"label": "Annualized Rent",
"options": "tenant_name_currency",
"read_only": 1
},
{
"default": "0",
"fieldname": "lease_sent",
"fieldtype": "Check",
"label": "Lease Sent"
},
{
"default": "0",
"fieldname": "lease_signed",
"fieldtype": "Check",
"label": "Lease Signed"
},
{
"fieldname": "column_break_22",
"fieldtype": "Column Break"
},
{
"fieldname": "marketing_fees",
"fieldtype": "Currency",
"label": "Marketing Fees",
"options": "tenant_name_currency"
},
{
"fieldname": "rent_end_date",
"fieldtype": "Date",
"label": "Rent End Date",
"reqd": 1
},
{
"fieldname": "indoor_rent_month",
"fieldtype": "Currency",
"label": "Indoor Rent (LE/Month)",
"options": "tenant_name_currency"
},
{
"fieldname": "outdoor_rent_month",
"fieldtype": "Currency",
"label": "Outdoor Rent (LE/Month)",
"options": "tenant_name_currency"
},
{
"fieldname": "unit_type",
"fieldtype": "Link",
"label": "Unit Type",
"options": "Unit Type"
},
{
"fetch_from": "unit.unit_area",
"fieldname": "indoor_area",
"fieldtype": "Float",
"label": "Indoor Area",
"read_only": 1
},
{
"fetch_from": "unit.outdoor_area",
"fieldname": "outdoor_area",
"fieldtype": "Float",
"label": "Outdoor Area",
"read_only": 1
},
{
"fieldname": "gla",
"fieldtype": "Float",
"label": "GLA",
"read_only": 1
},
{
"depends_on": "eval:doc.annual_raise=='Annual Regular';",
"fieldname": "annual_raise_type",
"fieldtype": "Select",
"label": "Annual Raise Type",
"options": "\nAmount\nPercentage"
},
{
"allow_on_submit": 1,
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"options": "\nActive\nHold",
"read_only": 1
},
{
"depends_on": "eval:doc.annual_raise !='Annual Irregular'",
"fieldname": "annual_raise_start_date",
"fieldtype": "Date",
"label": "Annual Raise Start Date"
},
{
"fieldname": "service_charges_type",
"fieldtype": "Select",
"label": "Type",
"options": "Monthly\nAnnually"
},
{
"depends_on": "eval:doc.service_raise_type=='Annual Regular'",
"fieldname": "annual_service_charges",
"fieldtype": "Select",
"label": "Annual Raise Type",
"options": "Percentage\nAmount"
},
{
"fieldname": "column_break_48",
"fieldtype": "Column Break"
},
{
"depends_on": "eval:doc.service_raise_type=='Annual Regular'",
"fieldname": "amount",
"fieldtype": "Float",
"label": "Amount"
},
{
"depends_on": "eval:doc.annual_raise=='Annual Irregular'",
"fieldname": "section_break_50",
"fieldtype": "Section Break"
},
{
"fieldname": "unit",
"fieldtype": "Link",
"label": "Unit Name",
"options": "Unit",
"reqd": 1
},
{
"default": "0",
"fieldname": "split_indoor_from_outdoor_rent",
"fieldtype": "Check",
"label": "Split Indoor from Outdoor Rent"
},
{
"fieldname": "column_break_24",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_28",
"fieldtype": "Section Break"
},
{
"fetch_from": "unit.outdoor_area",
"fieldname": "service_outdoor_area",
"fieldtype": "Float",
"label": "Service Outdoor Area ",
"read_only": 1
},
{
"fieldname": "outdoor_service_charge_sqm",
"fieldtype": "Float",
"label": "Outdoor Service Charge SQM"
},
{
"fieldname": "outdoor_service_charge_per_month",
"fieldtype": "Float",
"label": "Outdoor Service Charge per Month"
},
{
"fieldname": "totals_section",
"fieldtype": "Section Break",
"label": "Totals"
},
{
"fieldname": "column_break_32",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "split_indoor_from_outdoor_service",
"fieldtype": "Check",
"label": " Split Indoor from Outdoor Service"
},
{
"fieldname": "total_services_rent",
"fieldtype": "Currency",
"label": "Total Services Rent",
"options": "tenant_name_currency",
"read_only": 1
},
{
"fieldname": "items_section",
"fieldtype": "Section Break",
"label": "Items"
},
{
"fieldname": "rent_item",
"fieldtype": "Link",
"label": "Rent Item",
"options": "Item",
"reqd": 1
},
{
"fieldname": "service_item",
"fieldtype": "Link",
"label": "Service Item",
"options": "Item",
"reqd": 1
},
{
"fieldname": "column_break_25",
"fieldtype": "Column Break"
},
{
"fetch_from": "rent_item.item_name",
"fieldname": "rent_item_name",
"fieldtype": "Read Only",
"label": "Rent Item Name"
},
{
"fetch_from": "service_item.item_name",
"fieldname": "service_item_name",
"fieldtype": "Read Only",
"label": "Service Item Name"
},
{
"fetch_from": "unit.unit_area",
"fieldname": "service_indoor_area",
"fieldtype": "Float",
"label": "Service Indoor Area ",
"read_only": 1
},
{
"default": "Today",
"depends_on": "eval:doc.service_raise_type=='Annual Regular'",
"fieldname": "service_charge_start_date",
"fieldtype": "Date",
"label": "Service Charge Start Date",
"options": "Today"
},
{
"fieldname": "service_raise_type",
"fieldtype": "Select",
"label": "Service Raise Type",
"options": "\nAnnual Regular\nAnnual Irregular"
},
{
"depends_on": "eval:doc.service_raise_type=='Annual Irregular'",
"fieldname": "service_raise_details",
"fieldtype": "Table",
"label": "Service Raise Details",
"options": "Contract Annual Raise"
},
{
"default": "Today",
"fieldname": "opening_date",
"fieldtype": "Date",
"label": "Opening date"
},
{
"fieldname": "revenue_sharing",
"fieldtype": "Data",
"label": "Revenue sharing"
},
{
"fieldname": "insurance_policy",
"fieldtype": "Data",
"label": "Insurance policy"
},
{
"fieldname": "rent_sales_taxes_and_charges_template",
"fieldtype": "Link",
"label": "Rent Sales Taxes and Charges Template",
"options": "Sales Taxes and Charges Template"
},
{
"fieldname": "service_sales_taxes_and_charges_template",
"fieldtype": "Link",
"label": "Service Sales Taxes and Charges Template",
"options": "Sales Taxes and Charges Template"
},
{
"fieldname": "brand_name",
"fieldtype": "Select",
"label": "Tenant Brand Name"
},
{
"default": "EGP",
"fetch_from": "renter.default_currency",
"fetch_if_empty": 1,
"fieldname": "tenant_name_currency",
"fieldtype": "Link",
"label": "Tenant Name Currency",
"options": "Currency"
}
],
"is_submittable": 1,
"modified": "2021-12-25 01:52:49.738569",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Property Contract",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 545
- 0
muezzin/property_management/doctype/property_contract/property_contract.py 查看文件

@@ -0,0 +1,545 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe import _
import datetime
# from datetime import datetime, timedelta
import dateutil
import calendar
from calendar import weekday, monthrange
from collections import Counter
from dateutil.relativedelta import relativedelta
from dateutil.rrule import rrule, MONTHLY, YEARLY
from frappe.model.document import Document
from frappe.utils import flt, date_diff, getdate, cint, nowdate


class PropertyContract(Document):
def validate(self):
if self.is_new():
self.add_payment()
# self.add_contract_annual_raise()

def before_submit(self):
"""
Validates if property already on rent between the from and to dates
"""

# Get Property Contaract if from date is between new from and new to date
filters = {
"rent_start_date": ["between", [self.rent_start_date, self.rent_end_date]],
"docstatus": 1,
"unit": self.unit,
"status": ["!=", "Hold"]
}

property_contract = frappe.get_list("Property Contract",
fields=["name"],
filters=filters)

if len(property_contract) > 0:
frappe.throw(
f"This property unit already contain a contract <a href='#Form/Property%20Contract/{property_contract[0].name}'>{property_contract[0].name}</a> that not ended yet.")

# Get Property Contaract if to date is between new from and new to date
filters = {
"rent_end_date": ["between", [self.rent_start_date, self.rent_end_date]],
"docstatus": 1,
"unit": self.unit,
"status": ["!=", "Hold"]
}

property_contract = frappe.get_list("Property Contract",
fields=["name"],
filters=filters)

if len(property_contract) > 0:
frappe.throw(
f"This property unit already contain a contract <a href='#Form/Property%20Contract/{property_contract[0].name}'>{property_contract[0].name}</a> that not ended yet.")

# Get Property Contaract if from date is after new from date and to date is before new to date
filters = {
"rent_start_date": ["<=", self.rent_start_date],
"rent_end_date": [">=", self.rent_end_date],
"docstatus": 1,
"unit": self.unit,
"status": ["!=", "Hold"]
}

property_contract = frappe.get_list("Property Contract",
fields=["name"],
filters=filters)

if len(property_contract) > 0:
frappe.throw(
f"This property unit already contain a contract <a href='#Form/Property%20Contract/{property_contract[0].name}'>{property_contract[0].name}</a> that not ended yet.")

diff_days = date_diff(self.rent_end_date, getdate())
if flt(diff_days) > 0:
frappe.db.set_value("Unit", self.unit, "status", "Leased")

@frappe.whitelist()
def add_contract_annual_raise(self):
self.contract_annual_raise = []
start_date = datetime.datetime.strptime(self.rent_start_date, "%Y-%m-%d")
duration = self.duration - 1 if self.duration > 1 else self.duration
month = dateutil.relativedelta.relativedelta(months=duration)
end_date = start_date + month

for row in rrule(YEARLY, dtstart=start_date, until=end_date):
self.append('contract_annual_raise', {
'date': row.date(),
'amount': 0,
})

@frappe.whitelist()
def add_payment(self):

self.payments = []
start_date = datetime.datetime.strptime(self.rent_start_date, "%Y-%m-%d")
duration = self.duration - 1 if self.duration > 1 else self.duration
month = dateutil.relativedelta.relativedelta(months=duration)
end_date = start_date + month

if not self.annual_raise_start_date:
duration_to_add = 13 - start_date.month
months = dateutil.relativedelta.relativedelta(months=duration_to_add)
self.annual_raise_start_date = start_date + months
self.annual_raise_start_date = self.annual_raise_start_date.strftime('%Y-%m-%d')

annual_raise_start_date = datetime.datetime.strptime(str(self.annual_raise_start_date), "%Y-%m-%d")
service_charge_start_date = datetime.datetime.strptime(str(self.service_charge_start_date), "%Y-%m-%d")

frequency = 0
if self.type == 'Customized c':
if self.payments_scheduling:
frequency = self.payments_scheduling
else:
if self.type == 'Monthly':
frequency = 1
if self.type == 'Quarterly':
frequency = 4
if self.type == 'Half Yearly':
frequency = 6
if self.type == 'Annually':
frequency = 12

annual_raise_amount = self.annual_raise_amount
rent = (self.total_rent_per_month if not self.split_indoor_from_outdoor_rent else self.indoor_rent_month)
year = annual_raise_start_date.year
service_year = start_date.year
service_charge = 0.0

if self.split_indoor_from_outdoor_service:
service_charge = self.service_charge_per_month or 0
else:
service_charge = self.total_services_rent or 0

flag = 1

service_charge_frequency = 1
if self.service_charges_type == 'Annually':
service_charge_frequency = 12
if flt(service_charge) > 0:
for row in rrule(MONTHLY, interval=service_charge_frequency, dtstart=start_date, until=end_date):
if (self.service_raise_type == "Annual Regular"):
if row.month == service_charge_start_date.month and row.year >= service_charge_start_date.year:
# if row.year >service_year:
service_year = row.year
if self.annual_service_charges == "Percentage":
service_charge += service_charge / 100 * int((self.amount if self.amount else "0"))
else:
service_charge += int(self.amount)

if (self.service_raise_type == "Annual Irregular"):
ourdoor_service_dict = {}
for sub_row in self.service_raise_details:
date = datetime.datetime.strptime(sub_row.date, "%Y-%m-%d")
date = date.strftime("%d-%m-%Y")
ourdoor_service_dict[date] = f'{sub_row.annual_raise_type}_{sub_row.annual_raise_amount}'

month = '{:02d}'.format(row.month)
day = '{:02d}'.format(row.day)

fieldname = f'{day}-{month}-{row.year}'

if fieldname in ourdoor_service_dict and ourdoor_service_dict[fieldname]:
splitted_field = ourdoor_service_dict[fieldname].split("_")
annual_raise_type = splitted_field[0]
annual_raise_amount = splitted_field[1]
# print("===================>" + str(annual_raise_amount))
if annual_raise_type == "Percentage":
service_charge += service_charge / 100 * int(annual_raise_amount)
else:
service_charge += int(annual_raise_amount)

self.append('payments', {
'date': row.date(),
'amount': service_charge or 0,
'reason': 'Services Charges' + ("" if not self.split_indoor_from_outdoor_service else " - InDoor")
})

if self.split_indoor_from_outdoor_service:
service_charge = self.outdoor_service_charge_per_month
year = service_charge_start_date.year
service_year = start_date.year
if service_charge > 0:
for row in rrule(MONTHLY, interval=service_charge_frequency, dtstart=start_date, until=end_date):
if (self.service_raise_type == "Annual Regular"):
if row.month == service_charge_start_date.month and row.year >= service_charge_start_date.year:
# if row.year > service_year:
service_year = row.year
if self.annual_service_charges == "Percentage":
service_charge += service_charge / 100 * int(self.amount)
else:
service_charge += int(self.amount)

if (self.service_raise_type == "Annual Irregular"):
ourdoor_service_dict = {}
for sub_row in self.service_raise_details:
date = datetime.datetime.strptime(sub_row.date, "%Y-%m-%d")
date = date.strftime("%d-%m-%Y")
ourdoor_service_dict[date] = f'{sub_row.annual_raise_type}_{sub_row.annual_raise_amount}'

month = '{:02d}'.format(row.month)
day = '{:02d}'.format(row.day)

fieldname = f'{day}-{month}-{row.year}'

if fieldname in ourdoor_service_dict and ourdoor_service_dict[fieldname]:
splitted_field = ourdoor_service_dict[fieldname].split("_")
annual_raise_type = splitted_field[0]
annual_raise_amount = splitted_field[1]
# print("===================>" + str(annual_raise_amount))
if annual_raise_type == "Percentage":
service_charge += service_charge / 100 * int(annual_raise_amount)
else:
service_charge += int(annual_raise_amount)

self.append('payments', {
'date': row.date(),
'amount': service_charge or 0,
'reason': 'Services Charges' + " - " + "OutDoor"
})

for row in rrule(MONTHLY, interval=frequency, dtstart=start_date, until=end_date):
if (self.annual_raise == "Annual Regular"):
if row.year >= annual_raise_start_date.year:
# if row.month >= annual_raise_start_date.month or row.year > annual_raise_start_date.year:
if row.month == annual_raise_start_date.month and row.year >= annual_raise_start_date.year:
if row.year > year:
year = row.year
flag = 1

if flag == 1:
if self.annual_raise_type == "Percentage":
rent += rent / 100 * int(annual_raise_amount)
else:
rent += int(annual_raise_amount)
flag = 0

if (self.annual_raise == "Annual Irregular"):
contract_dict = {}
for sub_row in self.contract_annual_raise:
date = datetime.datetime.strptime(sub_row.date, "%Y-%m-%d")
date = date.strftime("%d-%m-%Y")
contract_dict[date] = f'{sub_row.annual_raise_type}_{sub_row.annual_raise_amount}'

month = '{:02d}'.format(row.month)
day = '{:02d}'.format(row.day)

fieldname = f'{day}-{month}-{row.year}'

if fieldname in contract_dict and contract_dict[fieldname]:
splitted_field = contract_dict[fieldname].split("_")
annual_raise_type = splitted_field[0]
annual_raise_amount = splitted_field[1]
# print("===================>" + str(annual_raise_amount))
if annual_raise_type == "Percentage":
rent += rent / 100 * int(annual_raise_amount)
else:
rent += int(annual_raise_amount)

self.append('payments', {
'date': row.date(),
'amount': rent or 0,
'reason': 'Rent' + (' - indoor' if self.split_indoor_from_outdoor_rent else '')
})

if self.split_indoor_from_outdoor_rent:
flag = 1
rent = self.outdoor_rent_month
if rent > 0:
for row in rrule(MONTHLY, interval=frequency, dtstart=start_date, until=end_date):
if (self.annual_raise == "Annual Regular"):
if row.year >= annual_raise_start_date.year:
# if row.month >= annual_raise_start_date.month or row.year > annual_raise_start_date.year:
if row.month == annual_raise_start_date.month and row.year >= annual_raise_start_date.year:
if row.year > year:
year = row.year
flag = 1

if flag == 1:
if self.annual_raise_type == "Percentage":
rent += rent / 100 * int(annual_raise_amount)
else:
rent += int(annual_raise_amount)
flag = 0

if (self.annual_raise == "Annual Irregular"):
contract_dict = {}
for sub_row in self.contract_annual_raise:
date = datetime.datetime.strptime(sub_row.date, "%Y-%m-%d")
date = date.strftime("%d-%m-%Y")
contract_dict[date] = f'{sub_row.annual_raise_type}_{sub_row.annual_raise_amount}'

month = '{:02d}'.format(row.month)
day = '{:02d}'.format(row.day)

fieldname = f'{day}-{month}-{row.year}'

if fieldname in contract_dict and contract_dict[fieldname]:
splitted_field = contract_dict[fieldname].split("_")
annual_raise_type = splitted_field[0]
annual_raise_amount = splitted_field[1]

if annual_raise_type == "Percentage":
rent += rent / 100 * int(annual_raise_amount)
else:
rent += int(annual_raise_amount)

self.append('payments', {
'date': row.date(),
'amount': rent or 0,
'reason': 'Rent' + ' - outdoor'
})

if not self.is_new():
self.save()

def on_cancel(self):
frappe.db.set_value("Unit", self.unit, "status", "Vacant")

@frappe.whitelist()
def create_row_invoice(self, rowid):
create_sales_invoice(self.name, rowid)

def get_brand_names(self):
list_items = []
brands = frappe.get_all("Tenant Brand Name", filters={"parent": self.renter}, fields=['tenant_brand_name'],
order_by="tenant_brand_name")
if brands:
for item in brands:
list_items.append(item["tenant_brand_name"])

return list_items

@frappe.whitelist()
def update_duration(self):
duration = self.get_dates_diff(self.rent_start_date, self.rent_end_date)
# frappe.msgprint(str(duration))

duration_months = (cint(duration["years"]) * 12) + cint(duration["months"]) + (
1 if cint(duration["days"]) > 0 or getdate(self.rent_end_date).day == 1 else 0)
# frappe.msgprint(str(duration_months))
self.duration = duration_months

return duration_months

def get_dates_diff(self, a, b):
a = getdate(a)
b = getdate(b)
diff_dict = {}
# a = datetime(a.year, a.month, a.day)
# b = datetime(b.year, b.month, b.day)
delta = dateutil.relativedelta.relativedelta(b, a)
diff_dict["years"] = delta.years
diff_dict["months"] = delta.months
diff_dict["days"] = delta.days

return diff_dict


def create_sales_invoice(docname="", rowid=""):
print("Starting Creation..........")
condiction = ""
if docname and rowid:
row_doc = frappe.get_doc("Contract Payments", rowid)
#condiction = " and P.`name` = '%s' " % docname
condiction += " and C.`name` = '%s' " % rowid
condiction += " and C.date = '%s' " % row_doc.date
invoices = frappe.db.sql("""
select *,P.`name` contract_name,C.date invoice_date
from `tabProperty Contract` P
inner JOIN
`tabContract Payments` C
on P.`name` = C.parent
-- and CURRENT_DATE() >= C.date
and P.rent_item is not NULL
and P.service_item is not NULL
-- and P.docstatus = 1
and C.invoice is Null
and C.amount > 0
{0}
order by date
""".format(condiction), as_dict=True, debug=True)
# return
if invoices:
for invoice in invoices:
s_name = create_sales_invoices(invoice)
print(str(s_name.name) + " Invoice Created.")
print("Finished.")


def create_sales_invoices(args):
# frappe.throw(str(args))
default_company = frappe.db.get_default("Company")
si = frappe.new_doc("Sales Invoice")
args = frappe._dict(args)
si.posting_date = args.date or nowdate()

invoice_date = args.date or nowdate()

debit_to = frappe.db.get_value("Company", default_company, 'default_receivable_account')
if debit_to:
si.debit_to = debit_to # Default Receivable Account
else:
frappe.throw(_("Please set Default Receivable Account in Company " + default_company))

si.set_posting_time = 1
si.postint_date = args.invoice_date
si.due_date = args.invoice_date

si.customer = args.renter
si.tax_id = frappe.db.get_value("Customer", args.renter, "tax_id")
si.debit_to = debit_to

print(str(si.postint_date) + "=================================================" + str(si.due_date))

# Accounting Dimensions
si.property = args.property_name
si.unit = args.unit
si.property_contract = args.contract_name
invoice_curerncy = frappe.get_cached_value('Customer', args.renter, "default_currency") or frappe.get_cached_value(
'Company', default_company, "default_currency")
si.currency = invoice_curerncy
conversion_rate = 1
if invoice_curerncy != "EGP":
conversion_rate = get_conversion_rate(invoice_curerncy,invoice_date)
if conversion_rate == 0:
message = _("Please Insert Currency Exchange for day {0}".format(invoice_date))
frappe.throw(frappe.bold(message))

si.conversion_rate = conversion_rate

default_income_account = None
item_income_account = frappe.db.get_value("Item Default", {
"parent": (args.rent_item if args.reason == "Rent" else args.service_item)}, 'income_account')
company_default_income_account = frappe.db.get_value("Company", default_company, 'default_income_account')

if item_income_account:
default_income_account = item_income_account
elif company_default_income_account:
default_income_account = company_default_income_account
else:
frappe.throw(_("Please set Default Income Account in Company " + default_company))

cost_center = frappe.db.get_value("Company", default_company, 'cost_center')
if not cost_center:
frappe.throw(_("Please set Default Cost Center in Company " + default_company))

si.append("items", {
"item_code": (args.rent_item if args.reason == "Rent" else args.service_item),
"item_name": (args.rent_item_name if args.reason == "Rent" else args.service_item),
"qty": 1,
"rate": args.amount,
"uom": args.uom or "Unit",
"stock_uom": args.uom or "Unit",
"income_account": default_income_account, # Default Income Account
"cost_center": cost_center,
"conversion_factor": 1,
"description": args.reason + " for contract " + str(args.contract_name)
})

tax_template_name = ""
if args.reason == "Services Charges" and args.service_sales_taxes_and_charges_template:
tax_template_name = args.service_sales_taxes_and_charges_template
elif args.reason == "Rent" and args.rent_sales_taxes_and_charges_template:
tax_template_name = args.rent_sales_taxes_and_charges_template

if tax_template_name:
tax_data = frappe.db.sql("""
select T.`name` taxes_and_charges,S.charge_type,S.description,S.rate,S.account_head
from `tabSales Taxes and Charges Template` T
INNER JOIN
`tabSales Taxes and Charges` S
on T.`name` = S.parent
and S.parenttype='Sales Taxes and Charges Template'
and T.`name`='{tax_template_name}'
""".format(tax_template_name=tax_template_name), as_dict=True, debug=False)

# frappe.throw(str(args.service_sales_taxes_and_charges_template))
if tax_data:
tax_data = tax_data[0]
si.taxes_and_charges = tax_data["taxes_and_charges"]
si.append("taxes", {
"charge_type": tax_data["charge_type"],
"account_head": tax_data["account_head"],
"description": tax_data["description"],
"rate": tax_data["rate"],
"tax_amount": flt(tax_data["rate"]) * flt(si.net_total),
"total": (flt(tax_data["rate"]) * flt(si.net_total)) + flt(si.net_total),
"base_tax_amount": flt(tax_data["rate"]) * flt(si.net_total),
"base_total": (flt(tax_data["rate"]) * flt(si.net_total)) + flt(si.net_total)
})

Sinv_ID = None
try:
Sinv_ID = si.insert(ignore_mandatory=True)
frappe.db.sql("""update `tabContract Payments` set invoice = '{0}' where `name` = '{1}' """.format(Sinv_ID.name,
args.name),
debug=True)
return Sinv_ID
except Exception as e:
print("Error......" + str(e))
frappe.throw(_(e))


def get_conversion_rate(invoice_curerncy,invoice_date):
conversion_rate = 0
exchange_rate = frappe.db.sql("""
select exchange_rate
from `tabCurrency Exchange`
where from_currency ='{invoice_curerncy}'
and to_currency='EGP'
-- and date ='{invoice_date}'
ORDER BY date desc
limit 1
""".format(invoice_curerncy=invoice_curerncy,invoice_date=invoice_date),as_dict=True)
if exchange_rate:
conversion_rate = exchange_rate[0]["exchange_rate"]

return conversion_rate


def update_unit_status():
units = frappe.db.get_list("Property Contract", filters={"docstatus": 1, "rent_end_date": getdate()},
fields=["unit"])
if units:
for unit in units:
frappe.db.set_value("Unit", unit["unit"], "status", "Vacant")


@frappe.whitelist()
def get_brand_names(renter):
list_items = []
brands = frappe.get_all("Tenant Brand Name", filters={"parent": renter}, fields=['tenant_brand_name'],
order_by="tenant_brand_name")
if brands:
for item in brands:
list_items.append(item["tenant_brand_name"])

return list_items

+ 10
- 0
muezzin/property_management/doctype/property_contract/test_property_contract.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestPropertyContract(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/property_type/__init__.py 查看文件


+ 8
- 0
muezzin/property_management/doctype/property_type/property_type.js 查看文件

@@ -0,0 +1,8 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Property Type', {
// refresh: function(frm) {

// }
});

+ 41
- 0
muezzin/property_management/doctype/property_type/property_type.json 查看文件

@@ -0,0 +1,41 @@
{
"autoname": "field:type",
"creation": "2021-01-15 16:58:13.694397",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"type"
],
"fields": [
{
"fieldname": "type",
"fieldtype": "Data",
"label": "Type",
"unique": 1
}
],
"modified": "2021-01-15 18:13:28.102488",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Property Type",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/property_type/property_type.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class PropertyType(Document):
pass

+ 10
- 0
muezzin/property_management/doctype/property_type/test_property_type.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestPropertyType(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/property_unit_detail/__init__.py 查看文件


+ 36
- 0
muezzin/property_management/doctype/property_unit_detail/property_unit_detail.json 查看文件

@@ -0,0 +1,36 @@
{
"creation": "2021-01-15 18:18:54.805424",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"unit_type",
"unit_number"
],
"fields": [
{
"fieldname": "unit_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Unit Type",
"options": "Unit Type"
},
{
"fieldname": "unit_number",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Unit Number"
}
],
"istable": 1,
"modified": "2021-01-15 18:22:32.111757",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Property Unit Detail",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/property_unit_detail/property_unit_detail.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class PropertyUnitDetail(Document):
pass

+ 0
- 0
muezzin/property_management/doctype/real_estate_offer/__init__.py 查看文件


+ 8
- 0
muezzin/property_management/doctype/real_estate_offer/real_estate_offer.js 查看文件

@@ -0,0 +1,8 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Real Estate Offer', {
// refresh: function(frm) {

// }
});

+ 273
- 0
muezzin/property_management/doctype/real_estate_offer/real_estate_offer.json 查看文件

@@ -0,0 +1,273 @@
{
"autoname": "format:{title}-{####}",
"creation": "2021-02-23 13:30:21.537656",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"title",
"details",
"basic_information_section",
"owner",
"property_type",
"age",
"city",
"street",
"postal_box",
"number_of_air_conditioners",
"the_role",
"owner_type",
"instrument_history",
"width_rating",
"column_break_15",
"plate_number",
"property",
"display_type",
"is_subject_to_installment",
"neighborhood",
"space",
"the_national_email",
"air_conditioner_type",
"the_number_of_windows",
"instrument_number",
"case",
"financial_information_section",
"price",
"limit",
"income",
"column_break_31",
"price_type",
"price_of_one_meter",
"total_price",
"mortgaged",
"amended_from",
"section_break_37",
"location"
],
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"label": "Title"
},
{
"fieldname": "details",
"fieldtype": "Small Text",
"label": "Details"
},
{
"fieldname": "basic_information_section",
"fieldtype": "Section Break",
"label": "Basic Information"
},
{
"fieldname": "owner",
"fieldtype": "Link",
"label": "Owner",
"options": "Customer"
},
{
"fieldname": "property_type",
"fieldtype": "Link",
"label": "Property Type",
"options": "Property Type"
},
{
"fieldname": "age",
"fieldtype": "Int",
"label": "Age"
},
{
"fieldname": "city",
"fieldtype": "Data",
"label": "City"
},
{
"fieldname": "street",
"fieldtype": "Data",
"label": "Street"
},
{
"fieldname": "postal_box",
"fieldtype": "Data",
"label": "Postal Box"
},
{
"fieldname": "number_of_air_conditioners",
"fieldtype": "Data",
"label": "Number of Air Conditioners"
},
{
"fieldname": "the_role",
"fieldtype": "Data",
"label": "The Role"
},
{
"fieldname": "owner_type",
"fieldtype": "Select",
"label": "Owner type",
"options": "Owner\nMediator\nAgent\nReal Estate Office"
},
{
"fieldname": "instrument_history",
"fieldtype": "Date",
"label": "Instrument history"
},
{
"fieldname": "width_rating",
"fieldtype": "Data",
"label": "Width Rating",
"options": "Commercial\nResidential\nAgricultural\nIndustrial\nTourist"
},
{
"fieldname": "column_break_15",
"fieldtype": "Column Break"
},
{
"fieldname": "plate_number",
"fieldtype": "Data",
"label": "Plate Number"
},
{
"fieldname": "property",
"fieldtype": "Link",
"label": "Property",
"options": "Property"
},
{
"fieldname": "display_type",
"fieldtype": "Select",
"label": "Display Type",
"options": "For Rent\nFor Sale\nInvestment"
},
{
"fieldname": "is_subject_to_installment",
"fieldtype": "Data",
"label": "Is Subject to Installment"
},
{
"fieldname": "neighborhood",
"fieldtype": "Select",
"label": "Neighborhood"
},
{
"fieldname": "space",
"fieldtype": "Data",
"label": "Space"
},
{
"fieldname": "the_national_email",
"fieldtype": "Data",
"label": "The National Email"
},
{
"fieldname": "air_conditioner_type",
"fieldtype": "Data",
"label": "Air Conditioner Type"
},
{
"fieldname": "the_number_of_windows",
"fieldtype": "Data",
"label": "The Number of Windows"
},
{
"fieldname": "instrument_number",
"fieldtype": "Data",
"label": "Instrument Number"
},
{
"fieldname": "case",
"fieldtype": "Select",
"label": "Case",
"options": "Effective\nClosed"
},
{
"fieldname": "financial_information_section",
"fieldtype": "Section Break",
"label": "Financial Information"
},
{
"fieldname": "price",
"fieldtype": "Currency",
"label": "Price",
"options": "Currency"
},
{
"fieldname": "limit",
"fieldtype": "Data",
"label": "Limit"
},
{
"fieldname": "income",
"fieldtype": "Float",
"label": "Income"
},
{
"fieldname": "column_break_31",
"fieldtype": "Column Break"
},
{
"fieldname": "price_type",
"fieldtype": "Select",
"label": "Price Type",
"options": "Fixed\nVariable"
},
{
"fieldname": "price_of_one_meter",
"fieldtype": "Float",
"label": "Price of One Meter"
},
{
"fieldname": "total_price",
"fieldtype": "Float",
"label": "Total Price"
},
{
"fieldname": "mortgaged",
"fieldtype": "Select",
"label": "Mortgaged",
"options": "\nReal Estate Bank\nA Commercial Bank\nInstallment Company"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Real Estate Offer",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "section_break_37",
"fieldtype": "Section Break"
},
{
"fieldname": "location",
"fieldtype": "Geolocation",
"label": "Location"
}
],
"is_submittable": 1,
"modified": "2021-02-25 11:58:56.669609",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Real Estate Offer",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/real_estate_offer/real_estate_offer.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class RealEstateOffer(Document):
pass

+ 10
- 0
muezzin/property_management/doctype/real_estate_offer/test_real_estate_offer.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestRealEstateOffer(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/real_estate_order/__init__.py 查看文件


+ 8
- 0
muezzin/property_management/doctype/real_estate_order/real_estate_order.js 查看文件

@@ -0,0 +1,8 @@
// Copyright (c) 2021, Havenir Solutions and contributors
// For license information, please see license.txt

frappe.ui.form.on('Real Estate Order', {
// refresh: function(frm) {

// }
});

+ 275
- 0
muezzin/property_management/doctype/real_estate_order/real_estate_order.json 查看文件

@@ -0,0 +1,275 @@
{
"creation": "2021-02-23 13:45:20.505685",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"address_of_request",
"details",
"section_break_3",
"customer",
"type_of_request",
"property_type",
"price",
"case",
"column_break_9",
"age",
"city",
"neighborhood",
"street",
"postal_box",
"additional_details_section",
"number_of_rooms",
"role_number",
"number_of_laundries",
"space",
"direction",
"security",
"central_air_conditioning",
"elevator",
"swimming_pool",
"laundry_room",
"column_break_26",
"number_of_toilets",
"number_of_halls",
"finishing_type",
"flooring",
"architecture",
"kitchen",
"car_parking",
"driver_room",
"working_room",
"amended_from"
],
"fields": [
{
"fieldname": "address_of_request",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Address of Request",
"reqd": 1
},
{
"fieldname": "details",
"fieldtype": "Small Text",
"in_list_view": 1,
"label": "Details",
"reqd": 1
},
{
"fieldname": "section_break_3",
"fieldtype": "Section Break"
},
{
"fieldname": "customer",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Customer",
"options": "Customer",
"reqd": 1
},
{
"fieldname": "type_of_request",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Type of Request",
"options": "For Rent\nFor Sale",
"reqd": 1
},
{
"fieldname": "property_type",
"fieldtype": "Link",
"label": "Property Type",
"options": "Property Type",
"reqd": 1
},
{
"fieldname": "price",
"fieldtype": "Currency",
"label": "Price",
"options": "Currency",
"reqd": 1
},
{
"fieldname": "case",
"fieldtype": "Select",
"label": "Case",
"options": "Effective\nClosed",
"reqd": 1
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"fieldname": "age",
"fieldtype": "Int",
"label": "Age"
},
{
"fieldname": "city",
"fieldtype": "Data",
"label": "City"
},
{
"fieldname": "neighborhood",
"fieldtype": "Data",
"label": "Neighborhood"
},
{
"fieldname": "street",
"fieldtype": "Data",
"label": "Street"
},
{
"fieldname": "postal_box",
"fieldtype": "Data",
"label": "Postal Box"
},
{
"fieldname": "additional_details_section",
"fieldtype": "Section Break",
"label": "Additional Details"
},
{
"fieldname": "number_of_rooms",
"fieldtype": "Data",
"label": "Number of Rooms"
},
{
"fieldname": "role_number",
"fieldtype": "Data",
"label": "Role number"
},
{
"fieldname": "number_of_laundries",
"fieldtype": "Data",
"label": "Number of Laundries"
},
{
"fieldname": "space",
"fieldtype": "Data",
"label": "Space"
},
{
"fieldname": "direction",
"fieldtype": "Data",
"label": "Direction"
},
{
"fieldname": "security",
"fieldtype": "Data",
"label": "Security"
},
{
"default": "0",
"fieldname": "central_air_conditioning",
"fieldtype": "Check",
"label": "Central Air Conditioning"
},
{
"default": "0",
"fieldname": "elevator",
"fieldtype": "Check",
"label": "Elevator"
},
{
"default": "0",
"fieldname": "swimming_pool",
"fieldtype": "Check",
"label": "Swimming Pool"
},
{
"default": "0",
"fieldname": "laundry_room",
"fieldtype": "Check",
"label": "Laundry Room"
},
{
"fieldname": "column_break_26",
"fieldtype": "Column Break"
},
{
"fieldname": "number_of_toilets",
"fieldtype": "Data",
"label": "Number of Toilets"
},
{
"fieldname": "number_of_halls",
"fieldtype": "Data",
"label": "Number of Halls"
},
{
"fieldname": "finishing_type",
"fieldtype": "Select",
"label": "Finishing Type",
"options": "Deluxe\nNormal\nSuper\nProfessional"
},
{
"fieldname": "flooring",
"fieldtype": "Data",
"label": "Flooring"
},
{
"fieldname": "architecture",
"fieldtype": "Data",
"label": "Architecture"
},
{
"fieldname": "kitchen",
"fieldtype": "Select",
"label": "Kitchen",
"options": "Open\nEnvelope"
},
{
"default": "0",
"fieldname": "car_parking",
"fieldtype": "Check",
"label": "Car Parking"
},
{
"default": "0",
"fieldname": "driver_room",
"fieldtype": "Check",
"label": "Driver Room"
},
{
"default": "0",
"fieldname": "working_room",
"fieldtype": "Check",
"label": "Working Room"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Real Estate Order",
"print_hide": 1,
"read_only": 1
}
],
"is_submittable": 1,
"modified": "2021-02-25 12:00:30.216679",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Real Estate Order",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/real_estate_order/real_estate_order.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class RealEstateOrder(Document):
pass

+ 10
- 0
muezzin/property_management/doctype/real_estate_order/test_real_estate_order.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and Contributors
# See license.txt
from __future__ import unicode_literals

# import frappe
import unittest

class TestRealEstateOrder(unittest.TestCase):
pass

+ 0
- 0
muezzin/property_management/doctype/reservation_repayment_schedule/__init__.py 查看文件


+ 82
- 0
muezzin/property_management/doctype/reservation_repayment_schedule/reservation_repayment_schedule.json 查看文件

@@ -0,0 +1,82 @@
{
"creation": "2021-10-23 15:09:39.197026",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"payment_date",
"principal_amount",
"interest_amount",
"total_payment",
"balance_amount",
"paid"
],
"fields": [
{
"allow_on_submit": 1,
"columns": 2,
"fieldname": "payment_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Payment Date"
},
{
"columns": 2,
"fieldname": "principal_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Principal Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"columns": 2,
"fieldname": "interest_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Interest Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"columns": 2,
"fieldname": "total_payment",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Total Payment",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"columns": 2,
"fieldname": "balance_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Balance Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"read_only": 1
},
{
"default": "0",
"fieldname": "paid",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Paid",
"read_only": 1
}
],
"istable": 1,
"modified": "2021-10-24 02:20:34.154918",
"modified_by": "Administrator",
"module": "Property Management",
"name": "Reservation Repayment Schedule",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

+ 10
- 0
muezzin/property_management/doctype/reservation_repayment_schedule/reservation_repayment_schedule.py 查看文件

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Havenir Solutions and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document

class ReservationRepaymentSchedule(Document):
pass

部分文件因文件數量過多而無法顯示

Loading…
取消
儲存