update calendar functionality and holidays
This commit is contained in:
parent
0620060066
commit
7395d3e048
49
custom_ui/api/db/employees.py
Normal file
49
custom_ui/api/db/employees.py
Normal file
@ -0,0 +1,49 @@
|
||||
import frappe, json
|
||||
from custom_ui.db_utils import build_success_response, build_error_response
|
||||
# ===============================================================================
|
||||
# EMPLOYEE API METHODS
|
||||
# ===============================================================================
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_employees(company: str, roles=[]):
|
||||
"""Get a list of employees for a given company. Can be filtered by role."""
|
||||
roles = json.loads(roles) if isinstance(roles, str) else roles
|
||||
filters = {"company": company}
|
||||
if roles:
|
||||
filters["designation"] = ["in", roles]
|
||||
try:
|
||||
employee_names = frappe.get_all(
|
||||
"Employee",
|
||||
filters=filters,
|
||||
pluck="name"
|
||||
)
|
||||
employees = [frappe.get_doc("Employee", name).as_dict() for name in employee_names]
|
||||
return build_success_response(employees)
|
||||
except Exception as e:
|
||||
return build_error_response(str(e), 500)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_employees_organized(company: str, roles=[]):
|
||||
"""Get all employees for a company organized by designation."""
|
||||
roles = json.loads(roles) if isinstance(roles, str) else roles
|
||||
try:
|
||||
filters = {"company": company}
|
||||
if roles:
|
||||
filters["designation"] = ["in", roles]
|
||||
employee_names = frappe.get_all(
|
||||
"Employee",
|
||||
filters=filters,
|
||||
pluck="name"
|
||||
)
|
||||
employees = [frappe.get_doc("Employee", name).as_dict() for name in employee_names]
|
||||
|
||||
organized = {}
|
||||
for emp in employees:
|
||||
designation = emp.get("designation", "Unassigned")
|
||||
if designation not in organized:
|
||||
organized[designation] = []
|
||||
organized[designation].append(emp)
|
||||
|
||||
return build_success_response(organized)
|
||||
except Exception as e:
|
||||
return build_error_response(str(e), 500)
|
||||
@ -1,7 +1,7 @@
|
||||
import frappe, json
|
||||
from frappe.utils.pdf import get_pdf
|
||||
from custom_ui.api.db.general import get_doc_history
|
||||
from custom_ui.db_utils import process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response
|
||||
from custom_ui.db_utils import DbUtils, process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response
|
||||
from werkzeug.wrappers import Response
|
||||
from custom_ui.api.db.clients import check_if_customer, convert_lead_to_customer
|
||||
from custom_ui.services import DbService, ClientService, AddressService, ContactService
|
||||
@ -10,6 +10,12 @@ from custom_ui.services import DbService, ClientService, AddressService, Contact
|
||||
# ESTIMATES & INVOICES API METHODS
|
||||
# ===============================================================================
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_estimate_table_data_v2(filters={}, sortings=[], page=1, page_size=10):
|
||||
"""Get paginated estimate table data with filtering and sorting."""
|
||||
print("DEBUG: Raw estimate options received:", filters, sortings, page, page_size)
|
||||
filters, sortings, page, page_size = DbUtils.process_query_conditions(filters, sortings, page, page_size)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_estimate_table_data(filters={}, sortings=[], page=1, page_size=10):
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import frappe
|
||||
from custom_ui.db_utils import build_history_entries
|
||||
from custom_ui.db_utils import build_history_entries, build_success_response, build_error_response
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
def get_doc_history(doctype, docname):
|
||||
"""Get the history of changes for a specific document."""
|
||||
@ -56,4 +57,23 @@ def search_any_field(doctype, text):
|
||||
query,
|
||||
[like] * len(conditions),
|
||||
as_dict=True
|
||||
)
|
||||
)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_week_holidays(week_start_date: str):
|
||||
"""Get holidays within a week starting from the given date."""
|
||||
|
||||
start_date = datetime.strptime(week_start_date, "%Y-%m-%d").date()
|
||||
end_date = start_date + timedelta(days=6)
|
||||
|
||||
holidays = frappe.get_all(
|
||||
"Holiday",
|
||||
filters={
|
||||
"holiday_date": ["between", (start_date, end_date)]
|
||||
},
|
||||
fields=["holiday_date", "description"],
|
||||
order_by="holiday_date asc"
|
||||
)
|
||||
|
||||
print(f"DEBUG: Retrieved holidays from {start_date} to {end_date}: {holidays}")
|
||||
return build_success_response(holidays)
|
||||
@ -233,3 +233,16 @@ def build_history_entries(comments, versions):
|
||||
def normalize_name(name: str, split_target: str = "_") -> str:
|
||||
"""Normalize a name by splitting off anything after and including the split_target."""
|
||||
return name.split(split_target)[0] if split_target in name else name
|
||||
|
||||
class DbUtils:
|
||||
|
||||
@staticmethod
|
||||
def process_datatable_request(filters, sortings, page, page_size):
|
||||
# turn filters and sortings from json strings to dicts/lists
|
||||
if isinstance(filters, str):
|
||||
filters = json.loads(filters)
|
||||
if isinstance(sortings, str):
|
||||
sortings = json.loads(sortings)
|
||||
page = int(page)
|
||||
page_size = int(page_size)
|
||||
return filters, sortings,page, page_size
|
||||
@ -26,6 +26,10 @@ add_to_apps_screen = [
|
||||
# "has_permission": "custom_ui.api.permission.has_app_permission"
|
||||
}
|
||||
]
|
||||
|
||||
requires = [
|
||||
"holidays==0.89"
|
||||
]
|
||||
# Apps
|
||||
# ------------------
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import subprocess
|
||||
import sys
|
||||
import frappe
|
||||
from .utils import create_module
|
||||
import holidays
|
||||
from datetime import date, timedelta
|
||||
|
||||
def after_install():
|
||||
create_module()
|
||||
@ -29,6 +31,8 @@ def after_migrate():
|
||||
for doctype in doctypes_to_refresh:
|
||||
frappe.clear_cache(doctype=doctype)
|
||||
frappe.reload_doctype(doctype)
|
||||
|
||||
check_and_create_holiday_list()
|
||||
|
||||
# update_address_fields()
|
||||
# build_frontend()
|
||||
@ -951,4 +955,54 @@ def build_missing_field_specs(custom_fields, missing_fields):
|
||||
missing_field_specs[doctype].append(field_spec)
|
||||
break
|
||||
|
||||
return missing_field_specs
|
||||
return missing_field_specs
|
||||
|
||||
def check_and_create_holiday_list(year=2026, country="US", weekly_off="Sunday"):
|
||||
"""Check if Holiday List for the given year exists, if not create it."""
|
||||
print(f"\n🔧 Checking for Holiday List for {country} in {year}...")
|
||||
holiday_list_name = f"{country} Holidays {year}"
|
||||
|
||||
if frappe.db.exists("Holiday List", holiday_list_name):
|
||||
print(f"✅ Holiday List '{holiday_list_name}' already exists.")
|
||||
return
|
||||
else:
|
||||
print(f"❌ Holiday List '{holiday_list_name}' does not exist. Creating...")
|
||||
us_holidays = holidays.US(years=[year])
|
||||
sundays = get_all_sundays(year)
|
||||
hl = frappe.get_doc({
|
||||
"doctype": "Holiday List",
|
||||
"holiday_list_name": holiday_list_name,
|
||||
"country": country,
|
||||
"year": year,
|
||||
"from_date": f"{year}-01-01",
|
||||
"to_date": f"{year}-12-31",
|
||||
"weekly_off": weekly_off,
|
||||
"holidays": [
|
||||
{
|
||||
"holiday_date": holiday_date,
|
||||
"description": holiday_name
|
||||
} for holiday_date, holiday_name in us_holidays.items()
|
||||
]
|
||||
})
|
||||
for sunday in sundays:
|
||||
hl.append("holidays", {
|
||||
"holiday_date": sunday,
|
||||
"description": "Sunday"
|
||||
})
|
||||
hl.insert()
|
||||
# hl.make_holiday_entries()
|
||||
frappe.db.commit()
|
||||
print(f"✅ Holiday List '{holiday_list_name}' created successfully.")
|
||||
|
||||
def get_all_sundays(year):
|
||||
sundays = []
|
||||
d = date(year, 1, 1)
|
||||
|
||||
while d.weekday() != 6:
|
||||
d += timedelta(days=1)
|
||||
|
||||
while d.year == year:
|
||||
sundays.append(d)
|
||||
d += timedelta(days=7)
|
||||
|
||||
return sundays
|
||||
@ -47,7 +47,11 @@ const FRAPPE_GET_CLIENT_TABLE_DATA_METHOD = "custom_ui.api.db.clients.get_client
|
||||
const FRAPPE_GET_CLIENT_TABLE_DATA_V2_METHOD = "custom_ui.api.db.clients.get_clients_table_data_v2";
|
||||
const FRAPPE_GET_CLIENT_METHOD = "custom_ui.api.db.clients.get_client_v2";
|
||||
const FRAPPE_GET_CLIENT_NAMES_METHOD = "custom_ui.api.db.clients.get_client_names";
|
||||
|
||||
// Employee methods
|
||||
const FRAPPE_GET_EMPLOYEES_METHOD = "custom_ui.api.db.employees.get_employees";
|
||||
const FRAPPE_GET_EMPLOYEES_ORGANIZED_METHOD = "custom_ui.api.db.employees.get_employees_organized";
|
||||
// Other methods
|
||||
const FRAPPE_GET_WEEK_HOLIDAYS_METHOD = "custom_ui.api.db.general.get_week_holidays";
|
||||
class Api {
|
||||
// ============================================================================
|
||||
// CORE REQUEST METHOPD
|
||||
@ -589,6 +593,26 @@ class Api {
|
||||
return data;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// EMPLOYEE METHODS
|
||||
// ============================================================================
|
||||
|
||||
static async getEmployees(company, roles = []) {
|
||||
return await this.request(FRAPPE_GET_EMPLOYEES_METHOD, { company, roles });
|
||||
}
|
||||
|
||||
static async getEmployeesOrganized (company, roles = []) {
|
||||
return await this.request(FRAPPE_GET_EMPLOYEES_ORGANIZED_METHOD, { company, roles });
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// OTHER METHODS
|
||||
// ============================================================================
|
||||
|
||||
static async getWeekHolidays(startDate) {
|
||||
return await this.request(FRAPPE_GET_WEEK_HOLIDAYS_METHOD, { weekStartDate: startDate });
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GENERIC DOCTYPE METHODS
|
||||
// ============================================================================
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="calendar-navigation">
|
||||
<Tabs value="0">
|
||||
<Tabs value="0" v-if="companyStore.currentCompany == 'Sprinklers Northwest'">
|
||||
<TabList>
|
||||
<Tab value="0">Bids</Tab>
|
||||
<Tab value="1">Projects</Tab>
|
||||
@ -11,8 +11,8 @@
|
||||
<TabPanel header="Bids" value="0">
|
||||
<ScheduleBid />
|
||||
</TabPanel>
|
||||
<TabPanel header="Install" value="1">
|
||||
<InstallsCalendar />
|
||||
<TabPanel header="Projects" value="1">
|
||||
<SNWProjectCalendar />
|
||||
</TabPanel>
|
||||
<TabPanel header="Service" value="2">
|
||||
<div class="coming-soon">
|
||||
@ -26,6 +26,9 @@
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
<div v-else class="coming-soon">
|
||||
<p>Calendar feature coming soon!</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -38,10 +41,12 @@ import TabPanel from 'primevue/tabpanel';
|
||||
import TabPanels from 'primevue/tabpanels';
|
||||
import ScheduleBid from '../calendar/bids/ScheduleBid.vue';
|
||||
import JobsCalendar from '../calendar/jobs/JobsCalendar.vue';
|
||||
import InstallsCalendar from './jobs/ProjectsCalendar.vue';
|
||||
import SNWProjectCalendar from './jobs/SNWProjectCalendar.vue';
|
||||
import { useNotificationStore } from '../../stores/notifications-primevue';
|
||||
import { useCompanyStore } from '../../stores/company';
|
||||
|
||||
const notifications = useNotificationStore();
|
||||
const companyStore = useCompanyStore();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user