From 44a03186dafc977ca87cba4ec8b8f01f39f7590a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=BF=97=E5=BC=BA?= <357099073@qq.com> Date: Thu, 3 Sep 2026 14:25:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0backend=E7=9A=84erp=E8=8B=A5?= =?UTF-8?q?=E5=B9=B2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/components.d.ts | 2 + backend/src/api/accountSet.js | 47 ++ backend/src/api/companyContact.js | 36 ++ backend/src/api/customer.js | 47 ++ backend/src/api/normalSetting.js | 23 + backend/src/api/notifyConfig.js | 18 + backend/src/api/supplier.js | 47 ++ backend/src/router/index.js | 518 +++++++++--------- .../apps/erp/ProgramManagement/index.vue | 209 +++++++ .../apps/erp/accountSet/components/edit.vue | 184 +++++++ .../src/views/apps/erp/accountSet/index.vue | 253 +++++++++ .../apps/erp/accountSets/components/edit.vue | 184 +++++++ .../src/views/apps/erp/accountSets/index.vue | 253 +++++++++ .../views/apps/erp/components/contactBook.vue | 453 +++++++++++++++ .../apps/erp/customer/components/edit.vue | 452 +++++++++++++++ backend/src/views/apps/erp/customer/index.vue | 473 ++++++++++++++++ .../apps/erp/customers/components/edit.vue | 452 +++++++++++++++ .../src/views/apps/erp/customers/index.vue | 473 ++++++++++++++++ .../apps/erp/supplier/components/edit.vue | 452 +++++++++++++++ backend/src/views/apps/erp/supplier/index.vue | 473 ++++++++++++++++ .../apps/erp/suppliers/components/edit.vue | 452 +++++++++++++++ .../src/views/apps/erp/suppliers/index.vue | 473 ++++++++++++++++ backend/src/views/user/notifyConfig.vue | 232 ++++++++ contact_screenshot.png | Bin 0 -> 125732 bytes go/controllers/backend_erp_account_set.go | 338 ++++++++++++ go/controllers/backend_erp_company_contact.go | 263 +++++++++ go/controllers/backend_erp_customer.go | 419 ++++++++++++++ go/controllers/backend_erp_normal_setting.go | 288 ++++++++++ go/controllers/backend_erp_supplier.go | 419 ++++++++++++++ go/controllers/backend_notify_config.go | 136 +++++ go/models/backend_user_notify_config.go | 32 ++ go/models/contact.go | 2 +- go/models/erp_account_set.go | 27 + go/models/erp_company_contact.go | 32 ++ go/models/erp_normal_setting.go | 48 ++ go/models/init.go | 6 + go/models/tenant_crm_customer.go | 42 ++ go/models/tenant_crm_supplier.go | 42 ++ go/routers/backend/backend.go | 32 +- go/services/reminder_scheduler.go | 120 +++- 40 files changed, 8166 insertions(+), 286 deletions(-) create mode 100644 backend/src/api/accountSet.js create mode 100644 backend/src/api/companyContact.js create mode 100644 backend/src/api/customer.js create mode 100644 backend/src/api/normalSetting.js create mode 100644 backend/src/api/notifyConfig.js create mode 100644 backend/src/api/supplier.js create mode 100644 backend/src/views/apps/erp/ProgramManagement/index.vue create mode 100644 backend/src/views/apps/erp/accountSet/components/edit.vue create mode 100644 backend/src/views/apps/erp/accountSet/index.vue create mode 100644 backend/src/views/apps/erp/accountSets/components/edit.vue create mode 100644 backend/src/views/apps/erp/accountSets/index.vue create mode 100644 backend/src/views/apps/erp/components/contactBook.vue create mode 100644 backend/src/views/apps/erp/customer/components/edit.vue create mode 100644 backend/src/views/apps/erp/customer/index.vue create mode 100644 backend/src/views/apps/erp/customers/components/edit.vue create mode 100644 backend/src/views/apps/erp/customers/index.vue create mode 100644 backend/src/views/apps/erp/supplier/components/edit.vue create mode 100644 backend/src/views/apps/erp/supplier/index.vue create mode 100644 backend/src/views/apps/erp/suppliers/components/edit.vue create mode 100644 backend/src/views/apps/erp/suppliers/index.vue create mode 100644 backend/src/views/user/notifyConfig.vue create mode 100644 contact_screenshot.png create mode 100644 go/controllers/backend_erp_account_set.go create mode 100644 go/controllers/backend_erp_company_contact.go create mode 100644 go/controllers/backend_erp_customer.go create mode 100644 go/controllers/backend_erp_normal_setting.go create mode 100644 go/controllers/backend_erp_supplier.go create mode 100644 go/controllers/backend_notify_config.go create mode 100644 go/models/backend_user_notify_config.go create mode 100644 go/models/erp_account_set.go create mode 100644 go/models/erp_company_contact.go create mode 100644 go/models/erp_normal_setting.go create mode 100644 go/models/tenant_crm_customer.go create mode 100644 go/models/tenant_crm_supplier.go diff --git a/backend/components.d.ts b/backend/components.d.ts index 96bb377..6fa544f 100644 --- a/backend/components.d.ts +++ b/backend/components.d.ts @@ -59,6 +59,8 @@ declare module 'vue' { ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElSelect: typeof import('element-plus/es')['ElSelect'] ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] + ElStep: typeof import('element-plus/es')['ElStep'] + ElSteps: typeof import('element-plus/es')['ElSteps'] ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] diff --git a/backend/src/api/accountSet.js b/backend/src/api/accountSet.js new file mode 100644 index 0000000..9d1740b --- /dev/null +++ b/backend/src/api/accountSet.js @@ -0,0 +1,47 @@ +import request from '@/utils/request' + +export function getAccountSetList(params) { + return request({ + url: '/backend/erp/account-set/list', + method: 'get', + params + }) +} + +export function getAccountSetDetail(id) { + return request({ + url: `/backend/erp/account-set/${id}`, + method: 'get' + }) +} + +export function createAccountSet(data) { + return request({ + url: '/backend/erp/account-set', + method: 'post', + data + }) +} + +export function updateAccountSet(id, data) { + return request({ + url: `/backend/erp/account-set/${id}`, + method: 'put', + data + }) +} + +export function deleteAccountSet(id) { + return request({ + url: `/backend/erp/account-set/${id}`, + method: 'delete' + }) +} + +export function toggleAccountSetStatus(id, status) { + return request({ + url: `/backend/erp/account-set/${id}/status`, + method: 'post', + data: { status } + }) +} diff --git a/backend/src/api/companyContact.js b/backend/src/api/companyContact.js new file mode 100644 index 0000000..9845f7f --- /dev/null +++ b/backend/src/api/companyContact.js @@ -0,0 +1,36 @@ +import request from '@/utils/request' + +// 获取公司联系人列表 +export function getCompanyContactList(params) { + return request({ + url: '/backend/erp/company-contact/list', + method: 'get', + params + }) +} + +// 新增联系人 +export function createCompanyContact(data) { + return request({ + url: '/backend/erp/company-contact', + method: 'post', + data + }) +} + +// 更新联系人 +export function updateCompanyContact(id, data) { + return request({ + url: `/backend/erp/company-contact/${id}`, + method: 'put', + data + }) +} + +// 删除联系人 +export function deleteCompanyContact(id) { + return request({ + url: `/backend/erp/company-contact/${id}`, + method: 'delete' + }) +} diff --git a/backend/src/api/customer.js b/backend/src/api/customer.js new file mode 100644 index 0000000..7412e24 --- /dev/null +++ b/backend/src/api/customer.js @@ -0,0 +1,47 @@ +import request from '@/utils/request' + +export function getCustomerList(params) { + return request({ + url: '/backend/erp/customer/list', + method: 'get', + params + }) +} + +export function getCustomerDetail(id) { + return request({ + url: `/backend/erp/customer/${id}`, + method: 'get' + }) +} + +export function createCustomer(data) { + return request({ + url: '/backend/erp/customer', + method: 'post', + data + }) +} + +export function updateCustomer(id, data) { + return request({ + url: `/backend/erp/customer/${id}`, + method: 'put', + data + }) +} + +export function deleteCustomer(id) { + return request({ + url: `/backend/erp/customer/${id}`, + method: 'delete' + }) +} + +export function toggleCustomerStatus(id, status) { + return request({ + url: `/backend/erp/customer/${id}/status`, + method: 'post', + data: { status } + }) +} diff --git a/backend/src/api/normalSetting.js b/backend/src/api/normalSetting.js new file mode 100644 index 0000000..cde06fe --- /dev/null +++ b/backend/src/api/normalSetting.js @@ -0,0 +1,23 @@ +import request from '@/utils/request' + +export function getNormalSettings() { + return request({ + url: '/backend/erp/normal-settings', + method: 'get' + }) +} + +export function saveNormalSettings(data) { + return request({ + url: '/backend/erp/normal-settings', + method: 'put', + data + }) +} + +export function resetNormalSettings() { + return request({ + url: '/backend/erp/normal-settings/reset', + method: 'post' + }) +} diff --git a/backend/src/api/notifyConfig.js b/backend/src/api/notifyConfig.js new file mode 100644 index 0000000..5b723db --- /dev/null +++ b/backend/src/api/notifyConfig.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 获取当前用户的通知配置 +export function getNotifyConfig() { + return request({ + url: '/backend/notify-config', + method: 'get' + }) +} + +// 更新当前用户的通知配置 +export function updateNotifyConfig(data) { + return request({ + url: '/backend/notify-config', + method: 'put', + data + }) +} diff --git a/backend/src/api/supplier.js b/backend/src/api/supplier.js new file mode 100644 index 0000000..d9a08a2 --- /dev/null +++ b/backend/src/api/supplier.js @@ -0,0 +1,47 @@ +import request from '@/utils/request' + +export function getSupplierList(params) { + return request({ + url: '/backend/erp/supplier/list', + method: 'get', + params + }) +} + +export function getSupplierDetail(id) { + return request({ + url: `/backend/erp/supplier/${id}`, + method: 'get' + }) +} + +export function createSupplier(data) { + return request({ + url: '/backend/erp/supplier', + method: 'post', + data + }) +} + +export function updateSupplier(id, data) { + return request({ + url: `/backend/erp/supplier/${id}`, + method: 'put', + data + }) +} + +export function deleteSupplier(id) { + return request({ + url: `/backend/erp/supplier/${id}`, + method: 'delete' + }) +} + +export function toggleSupplierStatus(id, status) { + return request({ + url: `/backend/erp/supplier/${id}/status`, + method: 'post', + data: { status } + }) +} diff --git a/backend/src/router/index.js b/backend/src/router/index.js index 1e23ac2..83376f3 100644 --- a/backend/src/router/index.js +++ b/backend/src/router/index.js @@ -1,256 +1,262 @@ -import { createRouter, createWebHashHistory } from "vue-router"; -import { convertMenusToRoutes } from "./dynamicRoutes"; - -// 静态子路由:需要在 Main 框架内显示的页面 -const staticMainChildren = [ - // 默认首页:访问 / 时直接展示工作台,URL 保持为 / - { - path: "", - name: "Dashboard", - component: () => import("@/views/dashboard/index.vue"), - meta: { requiresAuth: true, title: "工作台" } - }, - // CMS 文章/产品/解决方案是系统内置功能,不依赖数据库菜单配置。 - // 统一结构:index 为列表页,type 为分类页。 - { - path: "/apps/cms/analytics/content", - name: "CmsContentAnalytics", - component: () => import("@/views/apps/cms/analytics/content/index.vue"), - meta: { requiresAuth: true, title: "内容统计", modulePath: "/apps/cms" } - }, - { - path: "/apps/cms/article", - name: "CmsArticles", - component: () => import("@/views/apps/cms/article/index/index.vue"), - meta: { requiresAuth: true, title: "文章管理", modulePath: "/apps/cms" } - }, - { - path: "/apps/cms/article/type", - name: "CmsArticleCategories", - component: () => import("@/views/apps/cms/article/type/index.vue"), - meta: { requiresAuth: true, title: "文章分类", modulePath: "/apps/cms" } - }, - { - path: "/apps/cms/products", - name: "CmsProducts", - component: () => import("@/views/apps/cms/product/index/index.vue"), - meta: { requiresAuth: true, title: "产品管理", modulePath: "/apps/cms" } - }, - { - path: "/apps/cms/products/types", - name: "CmsProductCategories", - component: () => import("@/views/apps/cms/product/type/index.vue"), - meta: { requiresAuth: true, title: "产品分类", modulePath: "/apps/cms" } - }, - { - path: "/apps/cms/solutions", - name: "CmsSolutions", - component: () => import("@/views/apps/cms/solution/index/index.vue"), - meta: { requiresAuth: true, title: "解决方案管理", modulePath: "/apps/cms" } - }, - { - path: "/apps/cms/solutions/types", - name: "CmsSolutionCategories", - component: () => import("@/views/apps/cms/solution/type/index.vue"), - meta: { requiresAuth: true, title: "解决方案分类", modulePath: "/apps/cms" } - }, - // 兼容旧路径:articles/* -> article/* - { - path: "/apps/cms/articles", - redirect: "/apps/cms/article" - }, - { - path: "/apps/cms/articles/category", - redirect: "/apps/cms/article/type" - }, - { - path: "/user/userProfile", - name: "userProfile", - component: () => import("@/views/user/userProfile.vue"), - meta: { requiresAuth: true, title: "用户中心" } - }, - // 兼容拼写错误的路径重定向 - { - path: "/apps/erp/dashborad", - redirect: "/apps/erp/dashboard" - } -]; - -// 静态路由:登录页独立、home 导航门户独立、404 页面独立 -const staticRoutes = [ - { - path: "/login", - name: "Login", - component: () => import("@/views/login/index.vue"), - meta: { requiresAuth: false } - }, - { - path: "/register", - name: "Register", - component: () => import("@/views/login/register.vue"), - meta: { requiresAuth: false } - }, - { - path: "/forget", - name: "ForgetPassword", - component: () => import("@/views/login/forget.vue"), - meta: { requiresAuth: false } - }, - { - path: "/home", - name: "Home", - component: () => import("@/views/home/index.vue"), - meta: { requiresAuth: true, title: "系统导航", isStandalone: true } - }, - // 兼容路径拼写错误:dashborad -> dashboard - { - path: "/apps/erp/dashborad", - redirect: "/apps/erp/dashboard" - }, - { - path: "/:pathMatch(.*)*", - name: "NotFound", - component: () => import("@/views/404/404.vue"), - meta: { requiresAuth: false } - } -]; - -const router = createRouter({ - history: createWebHashHistory(), - routes: staticRoutes -}); - -let dynamicRoutesAdded = false; -let dynamicRoutesData = []; -let routesLoadingPromise = null; - -export function resetDynamicRoutes() { - dynamicRoutesAdded = false; - routesLoadingPromise = null; -} - -export async function loadAndAddDynamicRoutes() { - if (routesLoadingPromise) { - return routesLoadingPromise; - } - - if (dynamicRoutesAdded) { - return Promise.resolve(); - } - - routesLoadingPromise = (async () => { - try { - const { useMenuStore } = await import("@/stores/menu"); - const menuStore = useMenuStore(); - const menuData = await menuStore.fetchMenus(); - - if (menuData && menuData.length > 0) { - addDynamicRoutes(menuData); - dynamicRoutesAdded = true; - routesLoadingPromise = null; - return Promise.resolve(); - } else { - // 即使权限菜单为空,也要注册内置文章中心路由。 - addDynamicRoutes([]); - dynamicRoutesAdded = true; - routesLoadingPromise = null; - return Promise.resolve(); - } - } catch (error) { - console.error('加载动态路由失败:', error); - dynamicRoutesAdded = true; - routesLoadingPromise = null; - throw error; - } - })(); - - return routesLoadingPromise; -} - -// 核心修改:移除扁平化,直接使用嵌套菜单生成路由 -function addDynamicRoutes(menus) { - if (!menus?.length) { - return; - } - - // 直接转换嵌套菜单为嵌套路由(不再扁平化) - const dynamicRoutes = convertMenusToRoutes(menus); - - if (router.hasRoute('Main')) { - router.removeRoute('Main'); - } - - // 重新添加主路由,合并静态子路由和动态路由 - router.addRoute({ - path: "/", - name: "Main", - component: () => import("@/views/Main.vue"), - meta: { requiresAuth: true }, - children: [...staticMainChildren, ...dynamicRoutes] // 合并静态和动态子路由 - }); - - dynamicRoutesAdded = true; -} - -function findRouteByName(routes, routeName) { - for (const route of routes) { - if (route.name === routeName) { - return route; - } - if (route.children) { - const found = findRouteByName(route.children, routeName); - if (found) { - return found; - } - } - } - return null; -} - -function findFirstValidRoute(routes) { - for (const route of routes) { - if (route.component) { - return route; - } - if (route.children && route.children.length > 0) { - const childRoute = findFirstValidRoute(route.children); - if (childRoute) { - return childRoute; - } - } - } - return null; -} - -router.beforeEach(async (to, from, next) => { - const token = localStorage.getItem("token"); - const publicPaths = ["/login", "/register", "/forget"]; - - if (publicPaths.includes(to.path)) { - if (token) { - if (!dynamicRoutesAdded) { - await loadAndAddDynamicRoutes(); - } - next({ path: "/home" }); - } else { - next(); - } - return; - } - - if (!token) { - next({ path: "/login", query: { redirect: to.path } }); - return; - } - - if (!dynamicRoutesAdded) { - await loadAndAddDynamicRoutes(); - // 路由加载后重新导航,确保路由匹配正确 - next({ path: to.path, replace: true }); - return; - } - - next(); -}); - -export default router; +import { createRouter, createWebHashHistory } from "vue-router"; +import { convertMenusToRoutes } from "./dynamicRoutes"; + +// 静态子路由:需要在 Main 框架内显示的页面 +const staticMainChildren = [ + // 默认首页:访问 / 时直接展示工作台,URL 保持为 / + { + path: "", + name: "Dashboard", + component: () => import("@/views/dashboard/index.vue"), + meta: { requiresAuth: true, title: "工作台" } + }, + // CMS 文章/产品/解决方案是系统内置功能,不依赖数据库菜单配置。 + // 统一结构:index 为列表页,type 为分类页。 + { + path: "/apps/cms/analytics/content", + name: "CmsContentAnalytics", + component: () => import("@/views/apps/cms/analytics/content/index.vue"), + meta: { requiresAuth: true, title: "内容统计", modulePath: "/apps/cms" } + }, + { + path: "/apps/cms/article", + name: "CmsArticles", + component: () => import("@/views/apps/cms/article/index/index.vue"), + meta: { requiresAuth: true, title: "文章管理", modulePath: "/apps/cms" } + }, + { + path: "/apps/cms/article/type", + name: "CmsArticleCategories", + component: () => import("@/views/apps/cms/article/type/index.vue"), + meta: { requiresAuth: true, title: "文章分类", modulePath: "/apps/cms" } + }, + { + path: "/apps/cms/products", + name: "CmsProducts", + component: () => import("@/views/apps/cms/product/index/index.vue"), + meta: { requiresAuth: true, title: "产品管理", modulePath: "/apps/cms" } + }, + { + path: "/apps/cms/products/types", + name: "CmsProductCategories", + component: () => import("@/views/apps/cms/product/type/index.vue"), + meta: { requiresAuth: true, title: "产品分类", modulePath: "/apps/cms" } + }, + { + path: "/apps/cms/solutions", + name: "CmsSolutions", + component: () => import("@/views/apps/cms/solution/index/index.vue"), + meta: { requiresAuth: true, title: "解决方案管理", modulePath: "/apps/cms" } + }, + { + path: "/apps/cms/solutions/types", + name: "CmsSolutionCategories", + component: () => import("@/views/apps/cms/solution/type/index.vue"), + meta: { requiresAuth: true, title: "解决方案分类", modulePath: "/apps/cms" } + }, + // 兼容旧路径:articles/* -> article/* + { + path: "/apps/cms/articles", + redirect: "/apps/cms/article" + }, + { + path: "/apps/cms/articles/category", + redirect: "/apps/cms/article/type" + }, + { + path: "/user/userProfile", + name: "userProfile", + component: () => import("@/views/user/userProfile.vue"), + meta: { requiresAuth: true, title: "用户中心" } + }, + { + path: "/user/notifyConfig", + name: "notifyConfig", + component: () => import("@/views/user/notifyConfig.vue"), + meta: { requiresAuth: true, title: "通知设置" } + }, + // 兼容拼写错误的路径重定向 + { + path: "/apps/erp/dashborad", + redirect: "/apps/erp/dashboard" + } +]; + +// 静态路由:登录页独立、home 导航门户独立、404 页面独立 +const staticRoutes = [ + { + path: "/login", + name: "Login", + component: () => import("@/views/login/index.vue"), + meta: { requiresAuth: false } + }, + { + path: "/register", + name: "Register", + component: () => import("@/views/login/register.vue"), + meta: { requiresAuth: false } + }, + { + path: "/forget", + name: "ForgetPassword", + component: () => import("@/views/login/forget.vue"), + meta: { requiresAuth: false } + }, + { + path: "/home", + name: "Home", + component: () => import("@/views/home/index.vue"), + meta: { requiresAuth: true, title: "系统导航", isStandalone: true } + }, + // 兼容路径拼写错误:dashborad -> dashboard + { + path: "/apps/erp/dashborad", + redirect: "/apps/erp/dashboard" + }, + { + path: "/:pathMatch(.*)*", + name: "NotFound", + component: () => import("@/views/404/404.vue"), + meta: { requiresAuth: false } + } +]; + +const router = createRouter({ + history: createWebHashHistory(), + routes: staticRoutes +}); + +let dynamicRoutesAdded = false; +let dynamicRoutesData = []; +let routesLoadingPromise = null; + +export function resetDynamicRoutes() { + dynamicRoutesAdded = false; + routesLoadingPromise = null; +} + +export async function loadAndAddDynamicRoutes() { + if (routesLoadingPromise) { + return routesLoadingPromise; + } + + if (dynamicRoutesAdded) { + return Promise.resolve(); + } + + routesLoadingPromise = (async () => { + try { + const { useMenuStore } = await import("@/stores/menu"); + const menuStore = useMenuStore(); + const menuData = await menuStore.fetchMenus(); + + if (menuData && menuData.length > 0) { + addDynamicRoutes(menuData); + dynamicRoutesAdded = true; + routesLoadingPromise = null; + return Promise.resolve(); + } else { + // 即使权限菜单为空,也要注册内置文章中心路由。 + addDynamicRoutes([]); + dynamicRoutesAdded = true; + routesLoadingPromise = null; + return Promise.resolve(); + } + } catch (error) { + console.error('加载动态路由失败:', error); + dynamicRoutesAdded = true; + routesLoadingPromise = null; + throw error; + } + })(); + + return routesLoadingPromise; +} + +// 核心修改:移除扁平化,直接使用嵌套菜单生成路由 +function addDynamicRoutes(menus) { + if (!menus?.length) { + return; + } + + // 直接转换嵌套菜单为嵌套路由(不再扁平化) + const dynamicRoutes = convertMenusToRoutes(menus); + + if (router.hasRoute('Main')) { + router.removeRoute('Main'); + } + + // 重新添加主路由,合并静态子路由和动态路由 + router.addRoute({ + path: "/", + name: "Main", + component: () => import("@/views/Main.vue"), + meta: { requiresAuth: true }, + children: [...staticMainChildren, ...dynamicRoutes] // 合并静态和动态子路由 + }); + + dynamicRoutesAdded = true; +} + +function findRouteByName(routes, routeName) { + for (const route of routes) { + if (route.name === routeName) { + return route; + } + if (route.children) { + const found = findRouteByName(route.children, routeName); + if (found) { + return found; + } + } + } + return null; +} + +function findFirstValidRoute(routes) { + for (const route of routes) { + if (route.component) { + return route; + } + if (route.children && route.children.length > 0) { + const childRoute = findFirstValidRoute(route.children); + if (childRoute) { + return childRoute; + } + } + } + return null; +} + +router.beforeEach(async (to, from, next) => { + const token = localStorage.getItem("token"); + const publicPaths = ["/login", "/register", "/forget"]; + + if (publicPaths.includes(to.path)) { + if (token) { + if (!dynamicRoutesAdded) { + await loadAndAddDynamicRoutes(); + } + next({ path: "/home" }); + } else { + next(); + } + return; + } + + if (!token) { + next({ path: "/login", query: { redirect: to.path } }); + return; + } + + if (!dynamicRoutesAdded) { + await loadAndAddDynamicRoutes(); + // 路由加载后重新导航,确保路由匹配正确 + next({ path: to.path, replace: true }); + return; + } + + next(); +}); + +export default router; diff --git a/backend/src/views/apps/erp/ProgramManagement/index.vue b/backend/src/views/apps/erp/ProgramManagement/index.vue new file mode 100644 index 0000000..8577758 --- /dev/null +++ b/backend/src/views/apps/erp/ProgramManagement/index.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/backend/src/views/apps/erp/accountSet/components/edit.vue b/backend/src/views/apps/erp/accountSet/components/edit.vue new file mode 100644 index 0000000..224b0ec --- /dev/null +++ b/backend/src/views/apps/erp/accountSet/components/edit.vue @@ -0,0 +1,184 @@ + + + diff --git a/backend/src/views/apps/erp/accountSet/index.vue b/backend/src/views/apps/erp/accountSet/index.vue new file mode 100644 index 0000000..7cf6efa --- /dev/null +++ b/backend/src/views/apps/erp/accountSet/index.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/backend/src/views/apps/erp/accountSets/components/edit.vue b/backend/src/views/apps/erp/accountSets/components/edit.vue new file mode 100644 index 0000000..224b0ec --- /dev/null +++ b/backend/src/views/apps/erp/accountSets/components/edit.vue @@ -0,0 +1,184 @@ + + + diff --git a/backend/src/views/apps/erp/accountSets/index.vue b/backend/src/views/apps/erp/accountSets/index.vue new file mode 100644 index 0000000..7cf6efa --- /dev/null +++ b/backend/src/views/apps/erp/accountSets/index.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/backend/src/views/apps/erp/components/contactBook.vue b/backend/src/views/apps/erp/components/contactBook.vue new file mode 100644 index 0000000..ed7ce81 --- /dev/null +++ b/backend/src/views/apps/erp/components/contactBook.vue @@ -0,0 +1,453 @@ + + + + + diff --git a/backend/src/views/apps/erp/customer/components/edit.vue b/backend/src/views/apps/erp/customer/components/edit.vue new file mode 100644 index 0000000..c051d1a --- /dev/null +++ b/backend/src/views/apps/erp/customer/components/edit.vue @@ -0,0 +1,452 @@ + + + + + diff --git a/backend/src/views/apps/erp/customer/index.vue b/backend/src/views/apps/erp/customer/index.vue new file mode 100644 index 0000000..34e42d3 --- /dev/null +++ b/backend/src/views/apps/erp/customer/index.vue @@ -0,0 +1,473 @@ + + + + + diff --git a/backend/src/views/apps/erp/customers/components/edit.vue b/backend/src/views/apps/erp/customers/components/edit.vue new file mode 100644 index 0000000..c051d1a --- /dev/null +++ b/backend/src/views/apps/erp/customers/components/edit.vue @@ -0,0 +1,452 @@ + + + + + diff --git a/backend/src/views/apps/erp/customers/index.vue b/backend/src/views/apps/erp/customers/index.vue new file mode 100644 index 0000000..34e42d3 --- /dev/null +++ b/backend/src/views/apps/erp/customers/index.vue @@ -0,0 +1,473 @@ + + + + + diff --git a/backend/src/views/apps/erp/supplier/components/edit.vue b/backend/src/views/apps/erp/supplier/components/edit.vue new file mode 100644 index 0000000..884ecd0 --- /dev/null +++ b/backend/src/views/apps/erp/supplier/components/edit.vue @@ -0,0 +1,452 @@ + + + + + diff --git a/backend/src/views/apps/erp/supplier/index.vue b/backend/src/views/apps/erp/supplier/index.vue new file mode 100644 index 0000000..c2631c8 --- /dev/null +++ b/backend/src/views/apps/erp/supplier/index.vue @@ -0,0 +1,473 @@ + + + + + diff --git a/backend/src/views/apps/erp/suppliers/components/edit.vue b/backend/src/views/apps/erp/suppliers/components/edit.vue new file mode 100644 index 0000000..884ecd0 --- /dev/null +++ b/backend/src/views/apps/erp/suppliers/components/edit.vue @@ -0,0 +1,452 @@ + + + + + diff --git a/backend/src/views/apps/erp/suppliers/index.vue b/backend/src/views/apps/erp/suppliers/index.vue new file mode 100644 index 0000000..c2631c8 --- /dev/null +++ b/backend/src/views/apps/erp/suppliers/index.vue @@ -0,0 +1,473 @@ + + + + + diff --git a/backend/src/views/user/notifyConfig.vue b/backend/src/views/user/notifyConfig.vue new file mode 100644 index 0000000..4347792 --- /dev/null +++ b/backend/src/views/user/notifyConfig.vue @@ -0,0 +1,232 @@ + + + + + diff --git a/contact_screenshot.png b/contact_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..e37f554e3218f1bdd01fa1fb932f2e60f2edcb30 GIT binary patch literal 125732 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
5hS3qR}NOm>9reRt?S zdXg!NnZRS>>o2*Z>~D8qOcGc$E*(Juqv@-$kp#cz-U|)Z)8*ybKT9>VZ%ax$DAv(B zIBc~P=j7<;%k;2Nc9iQVC3ML{NP*>NjQyEG=1)IAbw*6u+T0AmOIHydAIL^{^OZOo zY=M`9!BQAICk}H7l5=pIzzJ_48ZHO6jA_Aw8QN$Rm9X>%nAI3JEkz3MU`cat)movV z#zKDr?Bk%lTU{yY4pcl{U7p2(6^@h{F+G1qYE$_rIW>iz^@$8D*GUs$NXLKVZ2H&m z`Q6(i&P3|zQ~7HJI(E@W>VDzBHd$Ul_hmvx)E(3*3gr0cWEm?3;iO4p<K^SQ2oE*AN`q;GZ8X4__2h%5+k1DN~ zp96ydX#(wJ?b|R+$2zVEzBPOS<7}UoV6FH}n_RBjVHIJpJp#yedd4?Spue56HLv(fpN--R%&W z5tC7Aars@GUZZ-Q{eJTl{IJ~=CPR3?z;g&!Y%J$~M6N+Xu?4wrp(IP@5O>LL(-_jx zTEb@x6Ag>>CcVP-ZbuUna5Bh!g28==(&u)vuLsd`C)w5Zc12j=)n`0c> ztpD_@e%H>b;%c#mM|+c0ZUFmipM9Dp$2jRzzQ?Hl^#Sm<#_0%Tz)kscYO?NNPU=$Y z1niMm*@HfTff0L>)eVy(`)?5Cjx^fMfbFje&l9TT!?X&PmlN(NKJcU^LsLE=KZq8C z_wyA(O(kW1ENJidQ*29e)!q)XM?<>I&wAtme|^Ps>)$KSn3PqYuV@fEe3Hq|rDF~= z&Dudt>*&Z5a+eSh=U?7wnSpkj&@aojx}8PRW!lwlV|2mr)GRJ4n$^->dTshNFPVxw z_TW(v&?_Z&4tef8Wrs-zKHQ2vZd+WxwG*GCnC*S{o|%Ft%-44-*+0?lP_^7FA|Ah5 zQ;=Kp7};Br=H7`I;<_>!KCHmMiO5->s1Kk=*_)8W?42_%AU z-PstS%71TunMPUCqxMKM`HcWQk67o)&Ubmo3V2#@qux>tT`s|(cUnHJgbPf_Y*7Wk zZ;WR~X#u`V#H!KmK_%j3+HrcYf*Hax)-_1R!=2jl9X87De0%^0IZ#+Ys>poM1Y2on z0$?@|BQ(>AMiWLv^v88D6h+jb)l-5>LO>?_&%R+eFKF8{rC7Ktd3JV2U|`VV5{lvr z)AIII87-q%Ogu_JZZ2I7<2{C{Qq5OgAxkAu-~%W^=nwm_NW!UxLc!Sm+Qx<{p{NP5ChS03jLAA22gn#Q&Up7?*&(mUT&ze)5E8Mpid#gs{Ite8yDxb zqwJC}N#bzR3!#XMIjP0^nCa!^+(*39uI@>5Fx%n(13_2}iAh%V9h-IVYq^22A1s9x z82HGstS{$&!91A00-{m>H_%Vo)j#Xr6@mxt^<^5Rtb_M?dD?t$#NkT)C4uV|G?u3D zyf4?Qw5hBfhdq)kV$o&n5jU1j*IGwETtHyi^Q1t5>;3;CBaS|ge*Erg1c`V&5X7-s}Z@Y4p^M;0wgbK@&Y5zPT;~{BosImUxwjR~j z8GI*-Tp+eH*}cx~!+Z*lxuXq(l~E2Yh?QdZwEKW3S|@Ge=;DyQ{PbP&8%2^clqDH4 zKkCO)ZNTh2CgkXPFyh!%5qRs)N~zEi*KfeS2g``~gogsnx5DxnO{{c$C@bwH+@mpp zm8{ksHXQzfoY!t;tqElw$;|Eb1z%+BgZl+4(ipVLMaa+S7C{%5?x~(dSLIz*6+w)- z_oJK8FDo^*scRC7AmkF5bu;UrbA>1C=0PA(b$B!Ss(jy+J&>0@@A?l-a4xPd~ zu0y|?xCau4%E<30^E_Ya&4mHYD=Gqmaa5Yjv22xsSRP00_nqH6vpo~G4X6YZEtIwh z)#&OdBwvJtKWY}gMTn&2(NQdorNI`(j6#SANGZbj6}{>LqYv+c2Q>+tFUWQMW{jTh z6B`-PPB?e5fk|oqxaMj6e|Z7=-@xj_{@_`k#Azb;CG)rEI>9@g85tCn2_cMk2lze{&mxkcVfcHht&a?)B7QBkL9QHh zrdT7YV!beq+HD&)Po%#j{#o4yiLJi{7!+m4I&Y>b9==?S1uMoriZxpiRiDL&9lLD{f7||8x`6Q zeF#%mU9LAM#AbXfir8MCh#{`PRn=5>@4IjIoT0fj)8|W6EPjmBv3fnFw!a92K7oD6 z+oJAJ$xudR@nq9nit5({qKpib6RCoRc7tP+P_Fu~9b-&DrTy0u zkUXF;@1u5}^u+Fmc0VyC8WZZ?rbdpd)ub=a{V)FPN!Ie_uNCzmnjnknRNEI4Af{>o?a>;}1f)jHmJ5pBsY9o8-n_~AR|K-L4o zA8e|*rr=UzQcCX|TZNf5gMxy6?LQZsVYub^?zIaCQk>oVi(~*Z3MB=z;fVvx^^vVU z47mAtcll29TNE&G-f`DgB=de5{r)}k>$6%b_0>NGzP(dlivC42IQ^FquuU8df;+Xz5(F{?XBEID^JQ@9<3i-9B zkqqn2U#&GDs_WKE+szRkLE$tUfK>brer``*cg@$-Q2X6?!e52|N?;$n2H+0`c7o_r zPaU1P?P;<$%CmL-q&CRFG{Y+{>vbAFPa8?k`yG^3K+wHS@W{VKdpci_YNLZSBRl~R zl!XRI*k%EJcptoTpIS6tBAS~s$tiq|{r%(g@~nDDXdaLOOGx%H0i@()_L!K38I&sT z{06f+oq-lX^T0_k8S)(YnVnCgOvMG^n#GSagS=((ufOEza%>yYsM%-Q5=dOXe)x+0 zFtmzldp_`NaoWH>H8~Z-)UF!fOu4Ii#n3drva+K6wg=vq=nI4lKH|zWdGpm~WR=Wz zs!p?9Y86(G8?OP+`50Dn%=xr5LdM$>Db+M;3ucY2uMZ?%czY!p;0#q^YCKU+Bzb8M zg?xs1xet8(uUN|`tKOM{H|PfyzlB3$qbZ&R!MH`lwCOX88v#RNjU$G`>P~^OC?n^j z%5R>>aE}-L5>Fj%LpmE%^W`~@>rXCbwZ)Cp5%5Dc5 z#OU+*QSI~dT+1)&2bC?jhhrJb2C?p;Xlc*>9n<;RC*WY}rnjp`Uf7;5_gy)roTZks z`8H4ix=Cz9SJ}(Uj=~XX@g)2{2oU$(K&%bg9;Tc{6x1DWEA`&!O&82w5KmPUa*L5 z@F{@LK-70|0WN8f?hNxn=9kC)9Rw>O=)9mvg=C_ZRKh?42RnNuzl7-QhMt#LX5f0j z{+9a3L*t|g&GHtFa_JQ3&x6P#A>?Xs+@d>i--W?M0It`gw*yJgk zEPCMy7iJlef#0mWdtAEQ=zf;-4P(Xo4EW`W)v*3Mmf?HkEzO z@Wolxw;tg|M!r<0B0&GmENF=P*M{_J@7HRr2b>fJF*;=VN|b_`Z4L)Bl|R*f%vOU* zRe1XSE3Sj!JsS`Ijpm0wKIDz#gZQy=vF3xxoL2}1#l`Y-^AEL{Zne2=CYkiULqkOa zkpxQCGr;=+>$@TPqq*MRdds+bj6A**XQ!bA|8gpJz;FS>>im#_pJF?8uepy?l%`XV zWH64X@AX38s7eIALj|oCzW$F!=V^FE60>W;=U)Nm3d!I4*EZ6U=e|ipFq|(?0QUBd zqnq3F;*zQN%6scv`lNU7avUC&s1^KP6b~#^Ow%dVslB~DZE3B%xzJdhRj$0N)8=cx z?iqfgFFLFS#D8&YuHpm7VA;Fu!8Tk zIz6+ksm@^1!_x%k7H$yB=Cp4+r1+SWm+vVghoHu^gZ<8kFfcD9CMG5|mB0Z9;;mfI zny>>NWc&JB7dL8h8Xy||UXcY~yXEA^f{br1S-#he8oN5$tt+TXKH1q*oI~n07OIc{ z4kKjwnlJk^g~v?D^SlIl5SM#j9kjk^b|nc<;LSc7y%8R_Ki(C51s_u4YFv?OEQ|7n zgirWgp^KbCLIFmoh7E9R-ZzgbXjt{QJjK}LcpjgvRT`Vx*2d+PDextPYLg{$;pEek zQ2g+Wj0zfnWm?SF4MA+82I5_#ZU$S@;E^@~AIa}dbGY`!(7z21wg+sb9jwJ;w%qxE zlT}aW*wwx4MSJT6*0}4JZ?Fa!6EL2_JqL)3)t)Dr5b>uJbd485j1?Se5R)i#ULU!* zI8&NPZ%2odC0WG#V_seyaE4-**z=I^O`lP`bK%=w3=;s2oxh_u|`5;wVjT?q~OL%vr#_t?YpWcQV`D;Th9kBD6Z zlaon}nT9pI1R*$mdiu^7FhV+Njroz09AUJ6@dWPpg{Hkxeg4R|VCrF{6H-uC zCJ}2vF}#CbII16@En_wFfi{q!6zDKl_+*Wg39YW%$3K~rvu}X5z>jTW5?8EQ=C(7M z6Cp>YJhFPP&Kplx+H^;#oIk1$W`5sC;!|6z6CTxqVkCOTSWfol1V z@Tt>*dK^5Wm&cOao5J777gAH~TzpK>pJAT^lNMe;rL}hEJw`^Z3IhhZhDY{4_^ywy zOwEe4Dg)B)dejO(bm4g%TDkV3p~-wi->t>_!Ywh87~Y#5HMO?m9upgt`R@Y^N1cAW z!kr;uH?xq@f@wQOY%cn#?vXPpn$_&6o@s<44hBYKcsLV<>hpooaUS#G<6q-AYzjSw z59S*bQhtcgO%!V?4`C4Sp`zkRc8Nyznzkv(=6Ex0MWv+)8uz#;FE2aAsDptUcB?;o zt>N^gtKl(MV4AH>=Bf7%KJI$tTw3DT-gh|sqWLY(T`M~~ogCvgJ(1*iaZzN#O=<;U zO-*w!ZdqI(j=&ud0tVn2>I(@e#l*y{&&%D=1}rWL#CKD5wW26^*Ood-?V4Tp-gfp0q!FI_iGV1it>JeZ4k>Up3l?O z8}R!Y?auw#dGjTf5#&e43rJ!J5 zoX8=MUu_ziW6C zlfgKf_CH#>1D=8vLrQAt215YSPU5&+$9vOz9cR_I&ONjo#^P4P?Axo-1z&Nt|Ck1} zY85UI88mas_At})rT?Ub|*;k`8+8m|*LAwo0EOU?d(}#sl zG8Ea1brRnoSDW8Oe25|=rIZ}q9Pv5puP~!Hw)Q$g7EytN%4MPtuLc%H>50cmeL@;* zP?*UdaSlUsiE6d_mqbPKM_}x`Rj7eqy*?=BYR8e zU^(+teP}-UMSBxOLV=+$_LbEj2T3r%{jtl2gPxvO-rL(~*iRaB*Z;>Cuw#%TftudC zwn(#4Q(5`F!so?GUFm0W|0#{c1XhhBomx5&DKj3|!d^;qV_TC>&shI6ahXYvX;%xw z>?&Kl=JV(5V%{U(yRhnT|EgDSNBKIcGtfyw?W-apEKM2Z67#PI$AN?N`7!?L_B7AO zS=a5!m!?ymzjihORrrof@ZQ-=r$(pMf0!5Yii(lm6yaFP?kRM&j#~)9Y1F00W}c%2~iZqd}_%6>CR9QQ;2tI;v8Qoz-X%D2V=co4JCWOb08PJ`e zu;5gd5JJf8?D_h2?6S%SsWtRRO6mLp#w*NoCGnxKpPzmGH3al6ZS5gp$IFef48fjv z)CMrX(lvv^ogFv$h6RqJM~Uc$97vB{f62_i;AkHDkv+1_a#!0dhBqmmom0Odgekh) z2u(IUw5_DXHbnL%^;i!B$DtnQOPn%0d80cB<*zX-E;EHajmI3mFJKy4@?Ymj24~0R zGM!k%bqVS6Y9GaZ{Cli_<@6#w_KG`73+$=tg5FFO#Tnh(7-3S8r!AznA@m&5oqJZJ zm(*Ywbr^rY5GK&}@50UKTkbI^NO`h(z?p?1psFr27#**O^e!1_Tts)TcCt!+K^eTnrsjI9ZKQz}YMROh1tno0t!B*>)9j9B+jk}~ zRPPg4c{MVj8IJZJ7Ztw)mK9IW)7#-hY;sbJ7SAf8-OBP+f~+ya!UhIje~RT_Z2fWe zTKoNQhLG@rd;;sAlTE=VX8uB!#2RTKhyzk?Y%i{O_yvo?V zJgKS|jqtRX_^>ocHZC8}9Lhhy>30lkGAWLPY! zFx7kPw(%O402e3Jdd+U7@=3!30|Wok2O3O(l=z8+ocwJjOj9^1W>_rOex3Yhh!fZ^ z??n+D>*!q^on3YMz_JRRc{p7pbnNLG^GBDx@3#L6`w#H!{3?o#I4B=@8^1WKm1T7F%#)Oo4*nJ6PWepZ;#%!1w2_ z!=cVAyMI0RC*lFU1vhK>`N32c0@4}K9v_@8(smy2{%EtmX@#T8c^(1|nA=anWu!!2 zvU76UjTdH57nK*e5OJwe7|URNs7p}i*lsed9PpJET7CZIGc&CTD8{F>wEGZDb*LjD zCZ@tA?K|UkOUX}p9j=g?Ps_MUB;7k~hghbm-N>L9OUVl8q0H-~FG(*2EuDQhFEVWZ z<3QvIScM-OBS{sNPUi4%biDP;!$`hKcJ!tB;?vcr}&C7uJi`QCfSyVF^UE5L#J z54Wx`*hi1o4g6uaPh~zl03g3-ZcV})qENwViIUq=8$5gcF1uXFERC=0p z8-mBf&sz8qKSS<$=Qw7fqxbB*485)Iydh zw9iH(LE(SQ`S`UV^=!>+)-rAw^5m(gGAq))<&w+q$fgYiHo^kv^NXiJ_M6`>>H~-F zDUxdz>kkdgrRr2L?{@e0HgmYH6G;d;p%!$=!I7{$H8JSbH@YW(>1J-Fs$U61cSEd%gp?f}=ERPMSx0F1I zrB^RUsOqGH2;6{des8p4S>XA=N5eLYb#(V%f@hen3BVo(Iiv+Rk#KjmK%Sd>#tI(U z^s`>egR|&LV6wr=i;M60K99ynEm>SivZeP~E|B&k8Z?kEZ zhdqsm4lh|QQ~9v^Md;u&3f-v=VokqxWaMoYwFIuL+LD7m+nVfzylMmQSy)(%=^O!= z<2+ip_$C~7h&I{5LmoFvop5qj=Fnun?P@71_qizk_>OvfO#6KNdm*a*9X$8D_3Z}J zuWt2w@6w?6WtcfcGxLoN4=3`8ef!$ookCTYplgf=Hw1o(kHXKZCwY6M%rhOUyu=2m z+PnThF-xK}aeIk5hVx6jkLsmz?kPIx85JR>siZM^f=`2`xMKAg7*vwI4_EpLD5OWI zgiQ+`jkw-lJXu6@#xX9c&<%hRgJ@5>2SQs>loXk}5D=MaJY4^C^q>X3xjQBsy=X?L z4!5=hlKYbkq4XO)4V5MzvhaUmQPLP@nHY)KXYyWCzqf5O^e-*FEmk1$H@Lt2?Z4u` zksDmt1~yeJHWEJv-l~kLddI3!vd_9Y35y1*I~rklwp2##lz!u?5O zG!c)BdIX;9`{gQaxvT)h@?9aIdG;L+B)K7SUW*$OYh^jPOg`^>V~@q-a+Vb6R{vV* z?m|B-iHPB|Y|$dpBLvtgNTyDg9uk{h^T$@2^pxl{{$9`GVI=}g2~hG=5);>`ar-Lu zlZG=w0ZPU)B%lI3KHy2b>%V--khhAoaV&IqduQw+-Tz0|Sw~gXf8AcXBt=v}It8Qz zLApyx>F(}sknV1zQ&H&<0cixJLK=aI=NDNTtgfD;cSuN>h;xCY?F#ZWPrf|8j^BT4Aqnd5n2 z4J$DStBExgt4YcrlOp)*&OM;dwH|(L*7JqFs721%Syz@U;JA<7`?GFwt9)I3&VUzf zJWg@zQ#p9ejAjCWqk3swP$~!XGlD)4NpJvVy86^;*@7(Jr9fZjy8oje+aHS5NBhaB zdRh*F#3-rb|3dZm?Wn%&zwyWRU(bs?$)vNUD<~+K5lA(p@VUHT)jdbK0ESLw2*m7T z$@o@V$L}6Sp0i&;W0l|e2EB|ApNoaPiAlO#k>}ogzWeT28HMrjeYIDZD@;Y7yjJR_ z`5p`E1!r_oAof{?qj!-Xjx1$phNb8w6EBmCh20hj1n&Vf@(?soOT`9|Oyz5Re=~`{ zIMr5rGMsxoGHobme)}hN1Mo|fZnrOjo3R2=ev|mA@nG7`xqA7DXB$96c;o77e+4|u ztr?i-YdmGIq0a%x+mE#@6b$5CKDp1e?h_MNq)CA*_~%DkXs(v|!QFOgOe-Yh??XWU z)ME-NjxLylO(DqA5S>b2Ibc2UPQmBOr!SsTv;O(XFF49OqW{D$k*DupY7BSc`>@0k zj(UUDM{N44(o>1pMFd-q3XLkNDlD=nLz%XoXjLF9RhoPukkiq{`1mD4{r$nKswiEZ zeSb&_7GI%`Xf(KCS4S$d>E=8MIy|<{AJ!KH6QgU_p5Yz3=(XU(@eR)VF5s6nU5OT2 zHN!YX?XujOtWtfWrrGF#zeY8$ou2jg?0ktbFT*haB;U%(pGr!)A6+uo^r-1p@v)SW zrc50Uko=WE5k1r7OZOaOrs;2Q$2ST1MyQl4zS(%!0VYWG56V!K2wPC|Q58k?E=lVH zlc7WWcg)6A_v4JnC8UIwEizqZjE1t!F&xLVF~GE@?!nP%OOLt|1EDvJC}m5i=Kw2LZ>OxfONFi zt#J7Mj~j12`AbF>Np-0#Ujj~}d8&>Eqcl?$UO<$;d1dt?=Lw_S=I6p^u2yf$Rb`() zFViZ1)ZmYk4lLFD@viWD6dZpk_6%eGXhrclL!>V5laVYW)s5ekor2G?sI(Mz^;*mG z7A8D!RLSWYCDdko+8zjFf#M#5RH{?Dq$Y0G&`z!8rFSv9@mG1J_WRI_Gg|9m3XD|{ zW>gg!x_TC55n^d!f%=sZrXFw)^&1plo&^Vpb{+hW`X+(vvLNz zd#1=OBV%&k%Y(ZrSIaUwfWUdTs{Gz>h3e|ApCHC_h-H@Uw?g1k7N?P zg3*WaNM;?AHsilTuw-&DG9p_L9m(shio*{Q^!2jGnH3KZEg0n4*w{$)5-qI=b;7F9 zZN`Z)CY@*9IjA{-nKb^#J1rnkQpvwnd*5}G_EL@PiNoh}SL+Q&Czp9fo?9JX z*w+Ba5FD_=d5s)ZaW4=?=}HEjAS66CLj{crWQvhoKE>ZE6N-NhvC@9=LPESKdW9>O@T`JjO5_5%*!G8;ar=I>5=r_pL7r%{%vRm>cx=nWkaiD-#A1pOf) zA&Q1+UU@M=n$hzBHi#%G_A76X?il13IXcvC5t=EftA~X|`JEVj3VkNi(D?QGE+;njeXc2W z3j2Z*x(DjB`_SSelMbh&WlH*r#Wl09_ml2TnL;K`^DM!^DS>^qc=`(k@<9=b zgR*xd5iLql0{fRQ^P!oU#2lX~D5xm`b;2ISJebMFY0vQAUBDf|H5pDM#OV2@n%VyT z#N!|3Dq-vq-jcERZ<>x^7biTQs-ieK#NveFIX{9c9F#U8J6rGzIEaN(IMC~jB2b&3 zeNSfnDH_^7RfDprOzO+>7_8v!=);NQhh7IwZtH^~X5o~?chHcE>J`H$AQ^(Xbck** z!ocW~ic-zxl2~nEqWC;fNZt++>4J-k9-Z{q(HLLIYGN$Wvf@~;GgS7%BzP;p?Rm}< z@CrXu{a#&Ge?Ms(xr_fp+3B zb-yBy5JwyH(emV(x*216x3`Hs^KW!u&d$*AWAOo35_zv(7`PG4DOVt6qCO#kgL9ALx=^I2()D*eXe6-h7@PHm8g}9D;R{brpSrxMlVK^e+VZ3!z;- z{i6S147#5)U-^c;U((Xe&?ThN&x`@T3Xm7eV7wFT{zbq0FdnU zR$Dx4a%$=cuu917zB|3T#wP76cmaDzPe@fxQcpS%G! zNFt=y*+1RSbU2*a^}XA4KbcXh#cTEAQ2b115?)(HbE~AF!QQfc_{*F_NTDx73hH)# zaQ|V4TF`gz1+QduNG*WNnaA>%FWjRVoKLO&7Xn_PP%tIvctDfxPY65*x1E`8i>5Y@ zlNsK9&+0R*Ep<;*pL7wcy+s(c3JP|FQEfjD3mL$B5=c}T>6rq)=gDN6jqcZ1%wopv z@lZT7UGY*hJbLEwC9=i|sshTY{@TJC;Q3y0XJu#_taqpTuM^kP`V4BgdjFMx4lSpF zi#^ow$C``KM%ROD#95M(5_2Sio^D{mug#xm7HIs5Y95yTV6rPR!Lo`@1$Kc$&Puf5$N7-Qq3a=xaM&`@qmpJC7!66aPZy z=rhqnT>(lc!~m`syl7+;B&h$VOM*o`w65-T9E~#*!tw*+2h@{Cm8IXz2S0fo6WeO` z4f7;&N8AHB_^Dg=Ib_eex5+;iH2q8IuJ+Lj#znv3d~+uVjAa&351`>qwgSV0%HAPM zJAkXhe>|`r`S$P4`sDmn7X5^fYnum)$aL9*-(qE$QcPs5*bqBHiNbvsh-96rNGO)k zBY97*Qig7nVfel!k2kA>!iRGG&f^qvzECZfem$X~ryWjb_XWcxBIG#7W=q=9H;y3) zu{+)Jw=>K|MrQKXi?1@&QZbVC1_C6)a2Y?GmfhJl9Z(y=nI?Sxv(x#3(VLRAN>-WsdU=UC%UP*CxT&1G^g_Vvdj zSXk7TXEdEwnMR!Ma^V4cK|M8Mgp|*%0?e7No8ydn6^%!OJ+ zo4QG-qew|D`FL4-lyzF2inki5Am5(m$;Puas0uU-YUDR%J&yBBR~2 zW^*N8jg!*YJUYVVFZgOFKa$O$!}??(mep(lh+gAS8M!a+DmQ>RAs{eF=>~&KVF`mC zh|BzH%I6liOK?8^y`MzSr0x}1X&5vNfE9p_ajH0RHS%!w_ zoXpz~cmSGDxj<;Nk=3g4nkz#T#D)hYOoa|0{7$F-E4eCmb9)4Stt7E#8L7 ziY+4}Um+YqTY@Sg{Cc`h=+m{s?;k97!0zzeI>p#m<+G#Z@jkK17&En+c;C$f*g97I zd;qJ6E%?~jgtpzYXuELYJyx}X-S=n8`p;378bml-({SA?c?Fb&0D+RJiU>_4V5zg^k=k zMqQ53dOB9x+L{Agxpe(zdD~BE85tP1uzCQf0m|9S_ind?FNAa4sE0+a#4HZFQt_@{ zV#OTckIA5x+K0bQ}|I@%@n_I7KZuSMwpL5ik@67Plnt#nq7`3-j z1`WX`rZvY7{*&lEALbeZJJU@q##_ZoMS{YhQ3X+DfCnV#;HZA5ZQ#F6RvBMha)Zqt zrMx=R8d|5nvg!+hdl_f2awMMUYgSf9<`{r3z*M4Zq9dy~dBmL|MQLN0N?~5ujB|) zw|7nq_BLqRc%Ab+_itlAT3e`MreJee5Jj7Agg_m^-mArYST2iy#eipj%B32CWbiE? z@J=WuKAhx2lcK*}(3Z=(D;OP7u33YMIxspKaci#6Ar+ce<;47=He<0 zkSwb*mE$SNRU~w_%=_?XU}XTxA?669P9;|0I6WUGb-u?O}Gzwhux{SWs>MF zp_BrK6ZQn;t{q~v#IT7sAtW)Dci!gKOojFkWw zd;iEncB^m_$mv${+0Uc9Lq$FPZoS$20o#F>0~eYrA2JfeM+>@P^~1|t==w0EAt<0&?t`niyH-XUbI9$OTH~PSBNp>^y58E6w5APb$|$CDB!>b@4vvcEkVW- z@6`#Gh(Gx;DXVt9!oml|DOZ-w?Zg?6@14#$;%<@8R!gq>)6tcHJ58K$y{f+I0k07c zNzZCKx@b2(wUqtLdRq3y*iY|=KRf-t(HXQiN@q~?Tk=?&-Q=yC-E$)%zCdHdbe+{Z z{WhYIuJK2VjO@u_Ve|wPBJ(8_&uWCrm5Xmk1Y71C_Zk?GB++c`#~Q1^&+_)iJ8_|j zt}byz)!+yg8a-!cXWDz34Sr(3p1G>O*Qg)-RtRmKunlEX9SPb{c=4jUewx*VjVmL@ zM&vsr#N&*~wY7huPnkZy^`diR9$krFnQDX2t2^@Enq~VzrDaVCG^0ihRLryU>_NE` z9UUF)P`x^Ai4@h;LOmWO;e7&&M+Rq!{jZN-jfQ4U9!}$eQ|5gN*GYI~TC~Y&W&Y}F z2{`>NX39pvkJcMa_JU4aFxZLv`pn#8sBbhQ*NH~Dl;zgj;Pyq!UA+f_^I((x|KcW4 zI-#*T64$}iwG$hu2>%YfXsd?InOUAO|ivNs7&Yd7e@j`q6?L`_vj9Nhz(A+Nf4cH>9?L9tJT2`RMc}=7j2K1 zQ!nGU2T&Lstt-K<6RZ<%vi)yAy8aHAcf64pcYClY?(A$$;!oAdrMX*LHY#%{5!e|U zi#;g=GsZdvFO$jX+26lt$VDZs^;K1Liy{}PA5DEa&^HA97Q13d+DiPTbGdzc>ieXh zs;lda@*MBKlwSw0?P%%idsqAS zXeZ`Z0DWqzbG`jsV6)M8wnzj>;o;wQ3J1&xL$Gm%Sw-j<_pS8%`dSdMGCR;QliwESfr{5q`PC`}sp zFz}eI06KfY%3VT6ELjcqoz$2t=Cn2zUrY zq7>%X`=$16SERZ{B_&jt4`L=BAQUodrMh{T9nS-_XtEc+iA60%G6%5g8tJ2(1b=sS z)jemU3k|@kyC;d@3S?7?bwS@%W;J1|K`!mf7hTNB6Ga~!zI76QhOZMdIP5k`S*mKc zsM6p3|9~QBh1L3xJ`|{YR2~bF18rpsLTHd*Xm0Ke3<}ez4h$4*N(gDvgf~2-nEybL zN4hYEM`Wtn8RlJwBo^&@3T7~)*pFAjLFKZ0-u3r*Rc-BO%&7eZRf}~ZQ9m2J5GtJNubm$h_9P zgKk@USO7lz5-HYo;Jx;=k7&1zaK+RIR`HslNr8q;7L0_Wu!N9^QMT=QO6ZnrW;0<8A7ND`b`AzbJwXyP| zvZIy5doTlnCIVF_N5?L}VRw)S2K$hO2`ZDpT`z}ggsZWsDU^|?=~{JKb(CVLDkFFD zp9%PK7Q6qfmv+a+{p6Z57_8yE7z}}N4-d0sLrzJ?mln>_;Yr_&Xz*C_TqGX}7qEHs zDK6fn5xUw?*$l?I$D1dW!leFKxd?p&Mb7vR{!(AxHh7LHQe=eu&k_$fOsB`o;c*t_ zO(13W!u9E<8(}-wlxofrx)J6kF?p_tTL|XL13dkxf`w>HB*<8Gzk(7=P$z2A3CF|w zDUYlbMmu^{Eb&rg*Z_di-3%4W98WplX>l!C8;G-{SDc>C4V4uf*!G&BU4L^EbBo{F zzTKJG@@H-;2p_*u7{;33n#6HRd#% zL$Nv;a$5J$GEp8r1IGG~7N!$ZC|-Uqrz72?tis*bH5|4p_5J|)$%WYt1Cdtq; z{_CN3kY;<~V1{w|+tRoU6;+m>6_(#(1W6pSEAT&jHcDgt!>m)(2PY$jfU-(cO3b&81I>TZ}e5-J7 z?HrC4YM`OfOG%mEYQ80k%E0iPYlQrnNqAyG0YtP}KcoN=wObZmYk0n$*y}Sj-sZW2 zEeV)3m|60Qye8{-G%p$-s6}GY5VI&ZR<>M%-etC!N+&;Ng8Ze^^!dv&Ja6CYSzpo7 zGnInegp0De|nTU*;%7U1-C7KGGUf4BNs9|I4HI<-34lq2k;aju^4UytUb?aRlC z&rdd@wgPcw1qnUuYt?Qq-1-)=VAiJ?k8XKC+Q!2NlTt$UBpk9$6h+c5fzaNfG( zk?DVWbatojzVCYnBwvvi2CHxeCYt<-OZ)^6(|-12w-W!_;5dz@cMvKz8;{+4xs#n= z1JOaKxVm-yzrjvY&?s&aaH(m~=3rlcJB5*^uk_)aQ~y!Ba1p1bE4Cek7dumLai8DA z$JU(O!!Hcq-^kA`z*BCe<-g(VKevKb-hQi;pdfA)+=pCV%<8$=Bw1j?Il7|Q1EEbu`x`2KL6JvT^q5>!5@4(}Bz2TP z1S}e5^RGTaMzOE2uewXcv>=VG2E=}}m%6&f9h@0$ONn?zJxi0lq(;Wm8XPjAmFS;n zWre$TQToIR+T3^NmL`j)ob4-v3=J0$0t3y5b5!0INh&Iwy^_u?LXceewY^1@X8?>; z%@13O;+gATSkqVs+!D<#@-aG9B_>693}QY^l48RtDJjr)N1>Pwjcc|)xgi#Z zg^ewC21}=~sMqYinK?O+d4gloH=3MT*uTNMtmifPvqtj2LoAw8Y(W7B@`iZG2(G+`M;C#5gSY$c!lZD}^~;u_Tp~t`i~&F1KAUU#I=ORWLA9~dV0e#26cJY@v;QajW95#qGvMGhk6JT&Gy%S zPq1QQMplNOz!67N2@KFZ(GNS{Ap`R9*GF5D>K7%i1X9k6C6BGXmx(K7=ZEDpv#dR_ z9qao3odwF}!MpVDl9KLJ{$7y-nd#N9HJvK*nPnJnWp=?;B+MzSibmf{c!s=8lta-u zzci1bvc8aPSKf@~r=%oP4su64l$M2n6z>?er)gjpH3KEnzGf~?PG!nX2OJ6t(>LpT zyPhGp+0bKaCe=6M{Jp6AvP6AxI7E+%6e-*a74@zG-4+-U4b0)kQQ@K5G+%B+c1)ic zf+MBi@7Zi33hM4xJ~uoSqbH1{g)d0ahy%DErMr3p9gbbe;Sj*Lt$wo9>ga- z)@*{6gNJ`=(5~&zFnP)ZaFFI*q}6ymlgZ%{exLd>IyI$KXEWty`!n_BW5&O!<9@(i zg>%+)y`}Ca4M&&HoyHl~W{wevggqfTcWXj89J~yn4&C5%@DTfw#B;I?Cv&_Fsdk1H_Uc=R&mY-P<{K_@52T;I&KHR~EV!@yU z36Y!2YNo8nsb|hz1jva>3mt9Oj_T9RPwZgw=;k@z{LvW+2M;iJByg|tJUh>SLWtc} zZk_gC0`uPfpFd%i;#%WujCc0Y|C}BFPUA4;D6(D-$0uDL!EHcqV3>fm%yWx)(o;&W`+X_g;q(5|Rm>#R<8SeaI(krA@Be zQv)OKUS_sA!=pzIOt*e06$iI4^Rqdqz~sNcc`UgvWjBw9F4_t48c@q9!ksM3E_Yu@PfSJz!>2<-bQcn9W2opi$;MFb#gZj@ z`Mu;J=74^syuA33fP=DIo~K~>hW;SKbVVgeqWJ7&(n~h4;T-{pgBGlLt8WcJ`6`U) z3*>kKfwy=vQ&~2u2f-G5|6au!{AySRzmAA)*cVJ*YH?YQQpd!lkUw>MuA{?mim+f@ zcCpB~_O+&@U#aBzgTE+PHk{W7uInR|Z{C=0Ea+AsNw7@glK0>RIBy!*3z0u5S{dt> ztH399x`#@Cx3lh6;zA)Qh$SKy2s}`$yE|RXi7nsl`7qA}D5Lts0jWPsDk9)0ufs&5 zCOhZ|`H7w)QBzyvVzqJtR;s(trQXl4?F!j#X;g5kg^H8~?90N=r4i+YFc2|(xGOrF zKo<6H*Kk!JPsCr@rOvl7zHS4qi71(k^e z5}c88qKUK{cXypaj2%9=k5+-_6eM6n?Rp-bXT_!dkZ-NedaeAjztXbNxk9K{PL%7u znlR+Js+M{mKD?KamF4+CH?2?^eGrZSNwQ$(#H)`Ydk^R4dzqqt2hvy}>qy5`SEnt! z9Yw~_OUuT1IY>Z$?E(i>wBf5 z{y3bD3&dIy#bbd!gOxuymp{)T*BAcnAt@<}A>rdP*>6j0YYGZJKA#{^IO}`9v7xY1 zi>8@>sL%;6>s$fv&_IZWp8i1?y+SKQVZBVZD1AW1%rUvr90k*gI6htLyhpfpcjoi)@nl!OhtF!Ca&Ww4d`tBc zL$!MEUuF#B2U54mL*2OSW=_G1K`Wcg_LNg)Xz;xvn-9`T0^GvN`x@ZHphNWme4UWK zKR+_j;lFAszGQ}$l`*)-nL>)@`+{M%W)wv%n^tJi3gG_zvVp7%PiW>r2X0zM&E3{{R7d%z(-u_f} z%7e&1nk{C>o5-|`BiI?TH>%w@#8qAX?7?pO&fRGi?T!dnwu;K%eF=po9$R#M;;oAk zsgl`O2d0s&fe0uq5bjwT=VA!e`Q)-8p-f=K`0HDoyWMDo-xT;%_J`)H z&&|jQu)!zcFqz!snuQbw{8;PKjUzJQj(PPd2C)YoQ8P2CJ>3HN6)zBcv`v;f)9-Hv zn>0W=lUyM3cuxbcu-yRSm4D6s`WMaUvbe2v*N-W>lJWJc_=iD<{@r$!1v>N46k<{S z`W;Kv-kTJr^0P!PTw)#rz%?E5@IKok9N@8=nFQvCyUpAUw^l#^i65=_ZpZrv-^|BS z1vm}vjXu%*j66`i0h?4Ut)wriqdrqtyLC&8i?Af0edDSfE%%M?1v0Ub%hstQI4ke7 zzCi}V)svCjNQ_lNVy~W96{%N{ZRJ6=n0iC|BXarkwAIc@-@C~X5Cb-Tld#>Cfba|a zVn65kwAdY~!YQH~VTijmM{<8Y4x0)Y@I#M?qvkU+2kG>vuU58RGa@r7+#@(nBcwG&xr3qX# zafK6Wo2|kR`mH@({9Xq%v9B;zs&)AqtmVg#_Ku3QX!m&YaQ6OekF-9lD74{#-mmm; z009Y!+JfsO;~=b*?u^wh{%2QcUNNlprYn)tTwcG)=s`D{hlGInO1~=96g#qHw|AIt z{Q}L9h*huP-%S|^A#3Y!-u>R!cR%7)A|$?*iD3I6>-Eg&)Ilw%GJWn29FTi|&QL``NHNL)L_ZYyfTV9SYfKbU~l2 zew|}OtD_B-2-WZ6SNp49vm);@)!I+k?dZ7uta!<23Xa?NazrZfU6$}#5@ddYY#5-M z&Y!5Y=&py%u&&aYhu`EI78^^3kE)5-jO6^${Wt1rJf&D=A6V{3GFh}m6T~DWiY2j0 z4lrv}at`#CLQ1=8aMtI9cr5Oy*-UZZj!gky19UZDv#}^u7XjYBpungz6r0a&{j*W2 zQgt@BaqNmttQA71W(~zK?DpxOB_CUjmkAp8xXAA+yyllDw6`(oJAFQ_yy8jH)l3vbh1N3y_O_niVP)?+S7TRL*M=K8AB{FGa1cYssnrZIM;Z~ryud{ zLARO>wgAk-z!4N2jQmqWJKsz0*G%YbvgjbGL9?%!Q;m+)i4tIr(WdU8Zm^Y5XsUK3 zm8!zDDqYT_sKolhs;|69(G`5VXa7X+Yty|0ZaW<^x9B8MwaC31eHcV5-|NiVJK`#4 zCmvvm^wzNd`Rl%GAP&saZ6WN*WCW!1Lodknl)lK4HS5Po@EZ$i5dLB)fGIg7fr}aR zvk!4BqaDgE*9o|lh~g)pA;pMR4^6QfK#h->%#-b!B>JcL>55-dQ@BBuXQR{E*n$|E zm+6(JfW`VFWyBR^an0}rZXdbHVWFpDJe4lN8Q~#?omBCfee>->c#IAdcO#0>41|BI zY%Iv*ZZqFPQ}ce;(fSQ{pRnCi_m-HU9i9m4J$__*EJ9}7loUCycUp#dAHu$;(uv*= zKL-R}B8JTCDH5=8oa}4HVFhDsvAWR*9pHCrKHR`LohcAvxZhA23;kn9NX#<>)^HqT zS^GI6A)5i#h;z-Y_WV4}L_{P07silW6ZX~(?Bpmtj_K<}I^8)|#p9Dm2 zK9mj+8L%e^%*Z(G(LH%U>W0l4#c*I0d&>`q$CW`~m@sxb!J&@LPnO`$=2EdCMG{3F zU(^&?B@%vvL7W%)iootRMi>%!t-_QDaDsvER0Bu$itfQd!u&-EVZs?l*ZZ2mh8xQ2 zTy?UyLWmpbqcAuYH!Cd+HP+3lo_I|%ngvT(x2UUN-XT4O7zaaegF|{!%|7uan@Qco zBl+~v7pH?PQoc6WV^JGY_bK8=ZyivwIWE_l1}1ohJS@GFImP8M_R-ZkRSfqt`q_i9 z5bEtp_k*H{+Tw`^B8bShFh{wn29*6zP?I=tIPw6{RQgKS;+fLMBzvK1>+7}hvSbly z4Me^Wm9U?+lsUd>=%1UagW&Da9ThI`hJZQ6WoGCQ&@ib+6X-Z;>NVo-#)4(g3l6}Z zW{1-cKe5fepn!2m@hw<7wqR}{efSy}pZ)uU4XsxQZuM^X`1rMq^Q)p?+fMNLB2Anx zCzMZj>2Grhd$K)#awRa{&zJx;CV{(-c>%vVSM(o4FG8)W^>VYjrHsrMLqo%vE3YNg zZ@JH0Gfzg1rT$-%1hwStwIZALi!-h_>h|CP4Ew7LR1XuqN2V`9%aZk;9=E)9itYRI z@3NF-(tsPI4v*{PN7uJOg~lsyRC}Vye4)l0Q+8ZC44XXj1eE7AR{ztzK+Ws>eN`)@ z{|04TIpPpjde2wb07Fi|`>4_N@JGs9bx~1KMg|!?e6oFRmThwVLZAO}NqM`zN;3Y4&RZ%I2x4)TKp5 zK9}cQa@TXd1BlmWGS{&Z5L?>-r^}R-nJY|Go(KA$&=24TJlE>WWVbTmpNO=DAIi}C zVD6J==hf=zdfRM9db-EP7;CQ3^~no*wLq!?<-hKV8bCEMkuklKG&TL~=48YK#1C*g z!O0&Ad}%*-c7p9;$KUHgrI?(YH)!5Q!Nqa{M9s{BFBa8Kh{?%!>&JhwAJUH%F$o+E z6kK+nfI$`Rll(_%j+F<%cN_NAJMmZ$4FiKy_pC7=x6K|qN6zcyWE#E$)Z7@z5l^~L z$(P5NXb)yAZMVSUz_~LtrVlX?YznI|A4bQ#$cx;7&`F7k)ktV!SsCA0I81?je8CYi`{`ip%ruUL7W~(46u(d2IT!vI`3QiHUw0a(p)}w`8?C6 zR_{MU(66(?*np&ep3N{SleZuKsGyP13D(o6%!H_3-tZ^U@Un@ zUor*B?GgyWkwf&;c+lzb&Nurl9@naS%0e-aMgBaz>u7&WY#)teqL9UHJe<=qKhV(` z#tyg?R-Hz4UVpeQ`*-LsE|CVmt{suHNjontZ}YjKFGHiGhK7dhf%`KL0=ZRdztEv;NYEmvF1))4YVfW-~IAu+ht{UnbbrTpp|Fe<_?YR=J>`_ze`o( zM|wT$`OyJeevV9rt8Mw|{^LGIM2yPFz3~dmLcyUSvfgPw^89>fJ81IDAjGWr^1>El zG7ls3PKai0hB+S`+_bf`z{LaWdF$yXNYl>DgKy3JX6};w;5_VfM%09K+iyGG3CtGL zJ)e&9Z>8?rvzO35UXkV|s^UD>r&I_B-EInWv{xzd>3t`-*C(EQX2G{CpG}$?9{fDmY`OpE0-aD;^8>ujlewYadm!*6C5c61==Q)waCyo915w;Co!>!7!vZSQL z8?3J(2eO5=LKWLnCQXjtQZbu9%D=$@66yNv#S?b)iawkzlneZ}%Bb(R3_pKv-{x~M z`civyNQY*N*30v6W^VPjt$?x9saG>lZD3{w!1^W7OGVDgb^c({N_}5=_doSOEjCk_ z(;``~ETcX!x$lfKGBB_~T#e2HyfO|O#%6Es;gb;&6TqU%d=>0Xw@(RlT74d*G z+Z_@b^!{IkGOLKpN7n0}cg=C*MG6D3Ti15{DmsexD1u2xKpj1f7^6L76VL+sSl z9@km_$?4nX1AH<~lIR=hO0^giB!VaBxwf0~8K@;wLnUn3KO z7b5`^_Oo!I{wUcndux3X$M7;iQ2OMtP*6XVyvg-OrL&hmGS0J7r9et6YD>GR!N2BR z6ZIuYj?uT*_nK>MM#p~ry7Pq(T63NSbct{w)9Rb7dO)6n8-qSTz#(m>p~PrN2Gja9+hps|?X1^`OJ_=StB z3*clB5iz+n<@nAQIstMMW1hA6sB3LI{hr(-GkiVU`iL(B$!Ttf_wo$9&QWcIqG%V1 zO7i`${lG>8Uy%3ek<7B-rLotg*RfpMqizbIk&XXuG~z**77&=>tF>p99CEsQ#KXfg z`_WfhTWYs|g{c~SG6C+o!5`!0=SuDc@ci)Jo2PsN1%$fyGXYzaE#OR)(L+kTzRuPY zI~5d!2t&5<0`$0^pKPl@+4Wel|65WoA5LM7$j7?P`ps@J)C_9$pT`GD4RyD+keRBH zyxURF%>7d0P|>UPe|O#n>1<|jJoyT2REc--^xF&yY4NP+QokHH&wYSjkCo*MGZvl{ zN8?_&Ex+aPx#MhuHaEik_hdh`-^vP{K2kBnmNqs|oR-6HkdKe>iZ9;FLRtk?{}nt} zu!u3gTnN?M6s!h=Nz89JwEEa%4mv`>@Y8Vy!T4pTff8xsC9+U#l4ph;r?5M!3PM(u zmmlI$*I^zEJCv@-&_w-XU_fM`uPIh+MGO4Y9JETsbyGn5sI%4w;hK6GABR+jPad$v~U6|y@Uy47s z3RqV~-|Shz7yk03g{i*z+&v-$0c~ zz2(_KO6`8LiKU4*TmML3It?`~ZTERr9RZm-Fp90y_@5QZ1gsI2^ml%{Ln2_YH$v~| z$ZtDcu>r&GBFE1eGlY7wjH!leVNoNhYlb*yt%goq&fBx}ph|9RT-8*`1O>Gtmj(LN6Ku zl`qcoD4g&`y?*URaIr-f_+Ore1;b*+=RDeSYd!!0iJ1H1%e2>HIs&^s)H{I)eq)ir zIwAHLkZrSca^fh@sY+OY5@Lz_hP=I^6b^OO)h3U9aEnX9D(&TyBydONFpQmj5sMwq zAD&0tX;4A6OP0SDyiXh$cm>_FY>4Xq{`yB+z%a#-l2X4OV{OBmE;Pd35n?FUA)qCY zgJ-nE=G$>Q-R4)|KqISwJ}i3kAr5gg*VhHuGjJ``p)Z=w=aoq6Euu*2@2xu@cUosY~shjjM1s4Nnvz1 z?!Y-FG7=N}r_;`iXTSe1Zx?lCWg5B38u#3grn57wd*wEXQ-+E6H^CGHs$cNzh)#ir z<14IXvUMnmfvRtaESDS%=xntVelrpc4MM{Kj(ws#E-8<6i4NnTanj6{5*{jITlhSc z4?}(>2V^&VFE752KVrX^h?&Qq#o6{e%tE$Cqn{y%nI3XY1p0Co@EcbQZ%gnpAz}QW zhkc(%_)~0eBKJ!o`dnKEo$9XbG#EwqzU;MEhNBeNbTysLebB1oc)~Ui4Uxt5>0+$c z$$x>E;pk`F=74XRM4|05GZKIMI!sE6d5V>=S!#H;C2-MsL_@5FAKulsi%gb_8(MdHtzN;Z4s+5Pgio2x?N6Ex|Y@8Iq3 z?z*!)MYT!4CiyYAE7H;FQ>))0v2CQ^sdklo$Hr{S&yzmeS0szF&nX0o_Ya4K$c5a3 zg=45?>}rHZ2R~uQe12d6il=;cPkRF}6KY5cs0cB82ek)d&rlT`)(4BSxhXtXq6x}g zA&x%7A-LEI*lnMi-vmP5n#oJWffBr1XQvhS8o)T%eYfhJ5SPV#v!fU^39zhckPY1? zcQoJu(2`VN4YPb_E-@WFy+Dk=>|<%O4_p$xKkWQ2zLf&f^K;GS2Fc5EohHN1P;kmK zQYBPZvId3S2F~Dh+F%>4h<+KSNj}lk{U;U_uT8uOaUsa9)#zaO$q$6|={j3Ee>a9w zt)VU#?rg%iJ?GyO_R@0RQnSv#-NygmETcHZ-rSDR44uXN)B5{nRo^7!X1D5XQFGAINyQm_%no)BBPbymcW&7cs77rAoL7>^Q8||&) zIldRxCW`m#z+Be8r8EfRM45Pm_WPokQ=bA#1zh*%!&Hy z1ZQnoE%%KwqpeTB}(A&bOfZTFdY4-Y105}kZ9Z5^06 z*Lj~L*{nFX)s|W`F}sCo)u-G;arr%)a?Vjl468(0Sy_t%NY*`kNMvZivHJBysNZ_) z9<|HnWI`s=^Ae5gedA{G-i)~{~(xJuGUzd@oV>?(TUE$D)A_)(5Z&iXM??z#GzExD@Q>AGMw%|U;hn!~PPl^;Tb~t*mvQxxZIsTDnyZmJ|_@KzM_t zzP|L{26n3BGY<1u-Z78;j&A;6{wI!pG=JE@C{?|s8Oo`Nx;6**s0A_#ivPv1&=V8t zuPm!NO+F!|b3yTKYC1+eEDQL7S=@|FOaPg9SD^+Klr`<+mW7gWE1NZj#)qpycPw)R zol)~eG{XLAt?#Q6PhCt@G!rMCtfF>;q_X&(><{ssV@UHD>4+-wY_tgNjGfiBPAda( zWSm~wCNzwk4NYRo${d>Sn$AdH)q&HEs+zzrfl9(bbj((9&&-TVJUe`Y7SwJ6!2J_o_bhSS4ZHi`H3sAVY@Au)h`>j<%#^a%1Sfy7CHIc!QK9W z1GH?&z;<_av2iSgh1gLhnp#b75e5n43YMt;PF3Xczd|JNnLkWpg@z_MpR0?@=08;7 zW)nQGdmbG2&moLcP7wxZkgQpmnu>XEH4ys>nny>)<(gZktqfyUf66j-*hO^INS48m+8Ju&q>w^I^ zpHMRtYZ}GbrF4xE zvshZFeo}0=bSNzZV|ac1c)0cxQvC}J%xfBij5?FEehAyJcdcT#nFWw|fAV8a0|S4| z-f-{7-Lz)WGp#{;VX{MD_f252loSmBrxY;>iL9YS|69WSmq4p9wDcS6aawr^OlV## zB^K?}Wx|C!H%Fr_=p(JL&H4eRTL0Ae`Gu<6ooF)B^)%to3mNVSM6d4Vj_1a82#H4k zINnH}->iFUX=z`qaAoxLP7ZA)D*<8*RFKu)`&?-5*`o9J^@Vt>Crmapw-0m^gO z?O6{Hu^jlQi$c0yKn~TU6Wz}A-qpOw5o{4bI2Cvew=1$?_Jw2dD)lQ8{`SMJHCQ&e z9;G{uhkeP(rRAsYYjppk2cdS;AQIA^=!{U(#6Q0$+}{MYLMR4JY7HnNYVUP2zS0Vl za(KiC2DWbVlK^>>8EJ!2tz&XB{3Lqgo5M>*oClVcyFjXcEFNp!@{u(AJAH{ry+}Mt zwc%DkLeM|*SQLXEKf-1}er+S=p!-VN`ogO+P9k($xe82H=& zRT)$IS)jLL5o&b~Z7RxLhhAGp_z~2ojAs?R6Lb5~tj?55 zU$n|6BQ~}kLg1ZYaRJ#Vf!Z|9>brWrf0r-O{wd3N`?ao`?5SNvPR{#ID(?&A%!o)1 zC~_%eQw|KldgyhsB@((X@T5GqvCw9!n#wNaS-D*ThGSNC_SV9}*{13yoE`N(d5j_@ zy-_Ty&IEU9MA@WbM=J9E&M$8$-czec#~<3;`4jLlx6PlzhJK!aM`JQs{e$#JXzYW+ zKWWZsrT|gQ*P6F#yyri+>$Q0Mu?ExVGwT(I!``gOebKBV(ZQ=VVzOPtZ zf!Q4J7uv!!pj|Ou+X0hwFM0e9%$=7l337}~;6|0s7TB6;diburOS&(4Xz#<{lbxPy zS2!$>iNAJj=Gf>RopX1>rBj<8vcOE1?OhsD-$SgV{pkRn=>a>9iy zE`5Li^tt>DI5L=?#CWs;oJWSr0w=(~=971e@oulOvaxfCb(K$o8d%3m;{t*$w<@zB z4Nb6vDiKfq-MFs1^|2&;C2sGuI$EOSHzI{9Gt7TBD+mc(g6sbIBY{L>bhMfQRLz$J z)r4tmY3=PPJuh1SA{n%%2PIk%!(eXa=ZH%63y&}+iH>W#~-jJ|IqOPeTiFNuS7s&jR) z&=QK4fx(L#7Y=8_W<2pxTtmnz@6K|*&_Or*&n|#CJ~`RM)V8ofC-KvLGO9us4g)Kz zNAxYVDCrp)tLp?^D(@@3X8HLdU4MOCCB4cU^gh|yp3E2M4-hdlhkYa&ert+H%$D~; zfkanO1=H}Q(R+S`Z@b$q^%|o<&0~&?iRXUl5P`o=|4g%C=r8t@$Cv_HG*6&eY;tw1?ipq8SyvZ87@y2nhh1_Rbqv%UYQk;K$*QncPqQ%8(y3u(e$ zvOc!g!yJjy3bCH&M?0om6JzF`=WuTSz4-t}!UIB{gbDq}aBlMozwNumnpS2@OguEx zM6Il^&w8~{6LE)&_x^~YI=%Gfblm`WuOI7tVql<5k5?^APGjUTc=%9YF%BGHqB4FI z{QQeUznX988hU%CD5ukP(f;9|W101Lnw+|wE~unJH89=?0MI5YJ^fePHTa~n$s)s` zQ4|AruDZ*Vol!LGWIKxRv601-tbi8%w$_gy&x^9kz>80dgoaHfB$7nSHZg~`b@1V- zAMJ}?C7Bv{R=}zemX=(2A{LeY}j&Z;4q1!FId#xwt{LNmC3#10^ zG9AdNd=Q6pL&#MUL#vp}^BK49D+tamq?3F=|G06EB(`|A*GfYrghEQUqg_$N15Sq# z{B}DltC&7Xayg<;_{?zi5Fz5x0aKxW+@0M+($XS1NRmvK!E&f}gH``~k)d3E!uL4? z9dmPYZ=ekH_lHKR1a4@eeUIxh^ zE{&oxlj4f@q#WZ@Yc##(m&wO%={8zTmRR*=)N|kHljyVlUW+16hj9AD! zVbNf@``3;+mpb-1Q&tupGXql`DHm{~p@Wr`1ZB!6iNL^51*MdCD=IPl>SBp0yLSh$9x^r~!gMr$~ zGS6vpQ@{L^?(Lryw~Jo>>c*)~&^r~B{xG=|_4i)`{OEXM-g%QL33qc4x1~exX#x_| z8O8y8a&B(W1ql|FkE^oZuPruEl!GUUjN>16446=OOp{OpL12fc9U0qnWESu+6@blZ zs-_Dm;Rtj0=ujl}D+WgZ$U-C%i{P>$A~ZEnhJA^69kZk<#ltmM{c$E!%`sAoWBQOeHCFB+nK|hM;S9e17b@4>5+TQ7=V{XUCvX9vLB&#bhxV zJ(xPd33?yKZu52tMA2)4cu1GsOHja|bqA7d2FIdUsnX+AmcV1|-}`qybIBZtZ!R$o zMIgSVOs0C zTo1v_@W%MP4xbrSok|(F7{ee7tuIyGq^w zpqE;sdW2XWzdLqZT-)0sytSS!Z1x^E&wj7M z_>Gq9v%gwlc+|t|&@NATm6fp)92&Q*|G1$+viI;fJwKPff#EX)z9wA})ENhY4C0dY zR001mm2lbp-bNk^=seP5yqaYlrG?BP(GpZC7bzg5yn%BZY*efW!}NcO_qZs4?W5J3 zAK_STZVelG-M|$ufwfBQX+lo8k1h^EMnLMRgZ`loUJk>&&Ag*>sMdqk)(8c^30*$t9}--us^MlU7YZU8KH&HV($DO zBmtQS(!n3v|7p>PQ0t}oKO`24{ch2p!CeEwI9g1VWqI91059o&Ia{ur%lQ)0DW~-6 zj!nYBjZV)@vDuL60@LR3rP~*D2xYp zjr@CKfPiT;Da$#rKe|Q7RFv-5!=sbY2r1}v_#f$FhjU6w@4h0D)Ng%`> z5S7VA%%A8UU-@K#5cW}_szBZ9B`B*wCf_@4V+(qe2^C8ed6ozzVkdP2GFYG(B=`;|;0hZ7Jk`Z`wgxg%-9o-6oMfXxplb28W2c5{NO%pUf zR20h=xPZ1#SU9w(h{ll+2Ifu89AQ_~;4?1*dc3oXONs#Ro&8%5ZZo4U;fcG~f9#j% z1S=WsrdI%+nH}=J1s(U+FrX7QzFuV>UR+#M4opq)%SL@_rT#E_A-`v3$GdJ-_~z2< z`ha79fNc>Ik!$d~g0h=En27K*SU04nOY7@cgH{`!XgPf0)cV2ptrLRYKXGi`zbolI&X%K`!I6hr`ab_Ptxo>X=` z7XY|N6q)CeWe#gta5`!IpO`S{B&!LYwFav zr*kK95E4Ao2?@dPPJ2Eiq0ga;Q2^Aqc}7tEzKiEyz}Em1xzva%_W#AfNZ&$1L4}v~ z-2<9^W!JUV?a%+{Qfj@XzZJs`F;ElJe}ybwTyiCTifT`I{{czqh!a?Ol-qPyft8P2;W5hs>faX1*aD3H?XE!%sHG(x zFcimuWV!y4d)I^Wf3zZELLxwPuXo#5!X5d#Fg0a{$O405R|=W8b8fXQWAynVgMmKT zY!B>pDSH-2$-jX>qS`N0mB7{aXZ1VtK4aI+FQ(q6u)vo~X|pGorfxtY1U(LA9st0f z_xHIi#o)i`yt17?8%dcn&Z{_9>v>Wp7p+&f&O1?#GwO7eKCuLn6>zBhMC+{pYkY2- zX(`nU-ka#S0PpPuqnZyLJZ3*x%sHt{k3S#tT8_V!k^&9tn5h?d-lXP1fdO*4u|x^9 zt8C}Mmio_91NsaLq}P2&>5#2W|(2SyA3Emr9q&FQK!%&8KAK^jZi~o3wT^Q zd-bV=t~@zAYw|n=KV-maIzc4-ub35uX^2G$kI#r|24WL`H$XtlGdv2_ zfg}b8TUr{Bi-Yj)f9$XaICa?LmT}Iv@``?dm!P2E4MKFpPbP8FFnDf*~gd;up_>`^whjvnI(J4QaX8QZy6IrCB zbRxszt^FdxDg^S^)Syjv4vuON(~y%ho>o_w-Wm{!f{Bj+TK42*BzPEmiSnqlY0)BJ z3MNdV&v9inC_*r{;(hH74sCW?BjfTUdD-i~z^3SulTUokARDR=irC)&PgF?s3*bx> z5*$1}I08z*n`Wr~Wt(O&;Q=KFkdOmo@9;DjWH#K323BzTDd+&KN{@!BYI+&J$=|aM zpzK>k8uEu%#qvU*VA5m~6yyhs{wA-Q4?4xbVbaQ`3%L4*KTIrbp^_*GW>&v>temi0 zPbPr1G1&R@-2tYQ3fjLw77#S_G;;?|i!S-GC}JU@RFfhY;VpR|4(}m$b7-%Btzk|{N zfFQg@?QV_>B5rxbtiOE0}4gg_cJ9YpPy%zMs1?&=fX3Eh#6C-e7yrDSoxDjLS-n1r#A8~*M8 zxMpC8&|C~T*_f7=wzxPrR6NVb&AtTF&KG*MKS3>!!qpWKLBS~jzS`Et21g4uT|g1j z<5T@hXw7cn+xLKHy=AB;uE0uS{;QiSSw6)s|MNVskxNq;^_pYS#Bgt7_jVh)yC8yw zy9P`e4jzPy>8UA@=#1?A=!v62Nr=!fS;n8LMR@ymGVt-%K@kVDZeV?VVQwzmg@(jX z7@9Hz_{2JLFkJ0xi(j;bB&N^QS$LYx}iUR!R=cud6>)sng)1 zzRy6Mo0x`yR45GA@rLKM22&9jYbzrB$qXL5hnKZBPy;VRxvq-~%^au}B5lJ$Lj&h> z;MUH&`gD+(T&dg8f8al}>(00z(n<%rFSY%jErgvQ`?Qn2{kS zh9}0!tWgH>4#a5{B5(YXP*b~vdX)abCh#$X{5>QWGPr)?^XLI%fl$&~?mUk4OVq=! zuZ*c~EEr1Nzz!0;ETrECp55*IUn$J=jWYVa^1viaP2C&<)R|7Sw6uM?T&%47_UoOJ zZd>N2oYPuxC@9ymE<=>;$9I$k0etA-srvB4`3vOXf2c?QG(FA|rhYn+2M_lTjUdng z36_SAPEB<2;5ZTqW45iOC6z%H>Sduy-D~%g1qduXnyp6F9O@Gywu1?64y&)Qi&c|7gSe8vX{v^dZJB?I7 z!ViU*2gga#Bv_v6^6%aaafrAnYFO;~`m$vXyE$FSo{s0W!75T)#Geav1R5iN=(4r7 zREkx$mzZn>z!he@ObH|;sY9@Z>{W4$iS17Z+!az`*Xx~Ob(4I3pt1=e5*@1CLCBZ& zoGSY)attCaL_ChJjRK!>Gn8`#EUc{+<-fqnYHFs8-~He-Ee6az>}kjIB?OVW=`sE0 z4LCd+9-dnOxDQeRzF5cfFBCW=iev&5d0^xW{9k}{gVAj85lJZygZwqQg-L_qO-@PR z6W_;694{|)oyZ_4=@^{%AJZkEDaL7=xjDG|NI>+M2}oo$Ae~Pl^!N97_Fg24MeaIzGorLL>C!Qu@1K-0MkLiGRzg}EWs5&l6=g+oWnm}pX&?D~EpQ2Stt_5% zZ$xq*e>WGkxplpI{OuS#{(Ijc`sX>|bm91UZg6zf6_nZs*|wS$5fSEwWY@<|QuAcY z48K?vKNNUJ1J;b4t@XbQHM2-GR3aW@*Y>q=NJwW23n=d1P9W#+cZfwb`i}J;=@NF& z#{YTmGlq>fL={k{QvOI3xd%zRSH%indwj1I6_UEBpo(>TGL9B1v|C(@LA9uw8jek` zWE=p(o24v7Xv1?{6X+y&fi1dTxQ@IPaKc5IYz<@f#*z}_f_Ot9$k&I>%|>P7y5XsM z1MV+lmJH34>N1=A1qBqP{JDs&BPV921Ktx-4lPnz%N9G*8(Qr4_Q|H1FlLjG#*+y; zzMV|lL66~7!7C^r=tjr^nF zDLDP7swZ5{!4@|GP7sq__006l)+x zP*5<3-`8`csd7bWug4#o@q3%s*-`Rbx}cZHA-8Q2+VwEvAJO(wERSyN*R|PxZ*>Ef zyQ$N~FYQmKg3W)-UqSM=ha#OoqshuSQe4b`JQ@euQe zJOuzLp(*drFVv>X0naGWvukUH%?@khLIqqV$Mq-cXc%J@i3g?#`$tqqfGWxlyhzmsKnYKM*4CX;&K< zHaiN(>No8DJ;y#YR8T;U7JcczJtv6=m=K9~UQCSsohxH*R9Rn_-1a_nxPF)UOr(%% zX+bl5o6sQf1_Ee@NBAGcR+`y&=N(pDc~qf;r?^4WIe1JdKnX?BV^}1InPCpu5WEn0 zy;uhl9{DLbg2N<%76q_l;0bR2Ub;_=`b8d?Y6D$3lZ++^F`qJ_>ZCubE&F(LWYNU= zdgH=-!zLj8*8ebiXo?&kdqMf{5Fkg9Aa;p%aHX>1MV{r&iBk7cvDJJJ$2Rr6_lg zV}qmKz8~Rg5SV~I%6v!fER2u}9_WBpFi5fMe7LDduu1imN)2{0z86-?{v~Hmu#$7O z1|CpkTs0yqv*Uw<(Rccd_TqZqC-?s$t(X+bxxd)03AcxpKdwr@yt-@~6A-Vsyv1S7 zxWPt5vYVHx$40`Uu!~a~FICP?wROcSc+qo>#_jUCtrW~GD%zhg!oa$jTA+98Nr{b; zQst&<2`k_8gwKMO+K+(qcX9k-wDf}`g2?!HJAWSU{>xQ0Bf=i#BTQHURZ1|IL}i|V z6hDPA$6}A<)E~1T&apGi1K>|GV3U4+i9{-RcN_i4ZZ$3h_0D3KN=^fa5HUraKXIm< zrpkivtr8+)^j9_`8Df#Z+v71N8XQTvZ6uEEo99UM9}Cg%-hmDNjk6arI1PeS-vl|v zlMDBXb8(QmK0lzwq6P>b7C!!+A)cLR?ZRF2&f#-OOjZKSg)dVw&br%|aA97`C` zI!jH-!C`&xDv|yO3atQS3YhMI$s09tRR{%mG_rQj=ZwP*N;tUZEuH~VA#a2$I=w0U zvLeudMQP_46;nKm`aNNMckGyBu%owHSn)dZHkU;=_?#FO^+|7##t1gHu5Ga9zE8v- z^=I7omVoDqZ$JW)$(4%6>L;u|z zh0;&M2k+=eHahyuw8dIoVra1X>1#r~w=p_euZuGsMBH#nq5Jotf#IX5bIY-S&)R7u zq8TVTIMyZpjCB13<9;lCy)-xVs3^k@A5@gW@`0S< zO{4d0wDjcMJcNp-x{0rkSpXRMl}6i519=Akc|@0A1|-&;0QbLt ztLNb;4}_YGu#bBwg!5^1V?wr<#}&nGd|aG3eL=XO=gG#|W{G+e3-pvqJee?0bFIX$ zjh~L6VyX-uBk{&E8&E@5LD7jNJo#B)7OHP!!+0_I2|8=JRvcQm<2Rtw&(6-$U@DVC z{#ozp^4TSNL1@jKliub1NQDanoCc>aQjWzcl)dMu#04NpIj%7B%|vTU_2uUCV?4RY zmQ!m>E5JCwvFW!ut}TP@SYqK1|HtuvsTvMIxn)4HsVC31^Ip7lGa<1@pS13qf1Bq< zEr?*YMomUw6U~q9N4y?|{)GQV#O8O4d^j5?c=$;gMBl{ z;1F@gKHobf2G2AfPWLpRsCr-BR#`tEapfi^x{>iS#h@{L`&QX#zV@JmbuXe9LosEreWriWqF!o^Hj?7E?|Hvj%Qw|7t0n6dRQ*Xeg zELCa^Pe#Io72sQp*}-}gXL|qseU!&RE_N8L_r+7Vt&8T5sik3?ztyBUWM^PG?e5{x zL$TQaqRbqM()kS*05dg<%db3Q8${BZDfszSr!tCKz0v$;{%L5Xgy_mUXT?Z z^r);7QnS#gs}4*TmV#aYF5U_PK>H%%V|6+rDj)KKWpw@C>GR|TwR#>CVYU0M|6Xfq z+Kxhug-4b6{-rQzVlo!YssPYrCu43?o$j$Wd^K}ZFOe<=34l?300^-B@NRltU+c=8Mz?YVEjz~>U* z1xd$b@;HpZA|^t-^i)j+y`ZC)MhRB(=Ld(Jx~2@ENwa!k%nuuo>19 z2uh7-a4O`*mzAE=QutsrA$?ccidnIr!hRv)b+iYXv5%|;St;yKcbf~xsmaNi_6A4q zBHut(v<1d__x5U$3b-%U8U>zN78Vq2kaQ0Z!?p%3KFP_CYwnb>c!-TeEh6M!-#oGz z;#r%RloY6q4ljih{G|v6bYYNo1<1Vft1VY*>VM+u$<~f#J3hO({HWeheU}m6oCy%^ z4x(Kj6D$EwPf8k;D4D``AWWFO60H|RGDifTELTMqr*JpV!;nu;x8QBCt)h)42AjO* zDp@^47XlDqu7H1rJ>$a5f#VnGA72tB4j^i=&n&^%N&ErQVl0aa(eF+L>MJt~yeTDf zGZD~;Zi&1DkdJ_8pVghZy6xT$vjSH5+TO7rArS93j399GKZJmJ zcs`+=5w^s;6t&=Iznc>|)QW9<`NF8F-9!Z%%AvJjjI&}DmsGsH6Y>U9GP2vf)66qr zAD4lsT&A&v35gzdbPOjz#b!`B5zV0{BpB+$C9KZ!Ih+Ri6z3*Bb|QK@0lbe9KMQ1T zqaAzO!jwdFpYI?4*uAw`O{-`Ec(1z$rHqr~ib?|=(1`zyGT=ZI_&99Oll}45Se9ae$#XqzO(4|87Ebl)VBO_ml zEQwB;i8`aiX{K|ob34vPX+stvq#1SRthE&x6>(cn27OdfR{2WM6)4(AJv@wteL59; zmqO#&@L~(NA4~CI0fQMtP3B!PUs#zyF8tgnL3w*D3IRzMA1i14Uiw3vKU7f!lXkV= z(X7qkK8LBOtFnQFL^Z8Ks_@6l>wy@fMF)V`*U;4X-ZcZ4>n`K(e0m{yNm^4 zG?^Kkb-`0iaJP4pnPO7Wt5wa(K^A6Atz^AF)AIaHQ^Rq+6PNhU;VvlwPS}dVW!O-M zPMv9o&+RHvmu|D!AR(6(rdF?+tu5^aVJd_QiE~DCGrc8lbK~Fm4)8(C@PnW+yF7?t zOo@$gqoS9FjvsD_*)0p=y${iwOA;?Em>Dh40oJfNl25WOd;W>B8dnuFTPafvs743i0$e@t#w1m`$$yM z5oUIuMsC6W@t!A!RdU}OF!;rV$A>rsivdW{T8vnv-0cKdST$Q5+OFG8fuu%gPFp&|@&}K66ds5DC z6u)-|21t1mtQ2xd2Du6dtkzLRAh2xWa|a?IMSKkUjZL>GZpe9(%ufkZ+dz!&Sr&O! z23dOx(1gE}+Eu=^??HmnX3#2<3-s7yxsmD8A}+X~7(#9sL95iPz*9s?wP1FE12Vi2 zrC^jH<&`FfAGzjSSc}WCq35Ou`U8syw^-a|zG<1+g(lMP19Cu$2OTBy=TrJcm#dy> zh)OZ43b1BUl%tt#|EMKTv_vhmm4io1g+sLkHp7}e@`9tf-8&l_fj0E)f6q-M5hLDm zb1S~D#rZi)q3{xL)dNu|hf#xq+CyV)YwK#>2vKDkx$y~A?o!g^_bNZ&n0~0|gJ_n+ zNG1%bQB%>?O%LaB`b5m{8kUifk%#8fvuaO6-|HJxGJ2(HJP|ckU#xIWvsg8o%U=%Z zL7+FHCJZa|e$NUzd5W`40nzY$`ecb~Q~8zxp1*En`WhY)sv9K=S1yli{IIAZMn$$gz8&6V1Y2F!lS+=%F7|;Rj;Z3 zK*SPk>lr=+t;E0!+=yVqW*>wG(Uh8<*HlYYfQ4z+1e%UoBvJ?&LLveR#!+Z9b?6C% z#vu{y@Ng|46s_Aa?x9D4%Zx@nIJ|)ic8BwzNJWI?i0@BtAAy1f!Y)SrP$=>G?%Og| zEmLAO=3#yJ@HBOedL&;wZYa@$4?b$|w2-n?`g&So#e;WHwrpTA=4x`XGbMZyy~V`2 ztyPSeR1oFtqaW44#*QXVUKe7emKmCHXnw!cfPGXlKuLmp9KE67g7N&}Jp_~_dd|9e zQ+}b=S~s#`lp0glZc(*x8fiiMXfRAymF%$)2J->z3smjDUiVjNC<}=c>T{G0Wa^cpF44(18~{MLlTQ? z>C>~FrR5&zi9{`4FQ(R;pE{g?IQM1!&4U-o;qzl9df{$X`iyYk%TvYO1pj&F>`=j9 z07k6&;D7OGv2Nb-dO@?13cc5A9dJijAVx%o$<^-v$ishMcGo9guorl1jt;(m;9q-c z(_&G%dht6WSxjhp0?u5}G6vi|?GqJ0yw*rSa-sK7=7Z@`LZrdP&966IeEfp&aB!sj ziL1>u`6N=6AE;$W-zkA*jBKdl<%MCpIU~4%Ia~is7ynvB9}!#pb`I_YBM@ZaF|hu# zKn;!H^3LTaXxg{8cs_pQSlY>IJp?s+Xzcp{0XG@PlV%@S`pr$unUb*~T)rHi|@G6hKbB`0qNR6EE8-IEN(Wq z?FGrND9a8K4Q3|;3p*)~dN#RYy*co8gHjUohC_2#8N8x1kUG z8KEnW_xA&-ci}7$H$Ld1C|E2#|EtCUsHDt0LIH|)EQq4p2Fq)gN${W(ct!>I_;!;q zZE{4ljron%8BZWj>a6NsL5;~@c@oyH#;9kJdt%VE(#*7vL%x*GYajLd;a(wb)~MZ` zk;3>()+x-&KVQX4+s7+QF!s@%m9`4h&{SG@SF;s=$LVG~>8h>$7ZhBomc2_2`--_7 z*rmY>LV4Y3@I7tdOcnJ!`4Jh3@BTUZ^J&!ryoZn<=v&;7)9THK6UojTSDKtAyc6pz ze$nY#FM~*KN_43qo_sNoR|B2Pu>;y|u=@T62BGYfgZld$G{T{LKoW^ zp@8E1cp*^C);5Di8_dtiMSO66upGCgA>+ZQd}t6z%n5iwHvS8E!_~UjUwn8y{`~d_ zMLd+kA5N|>t!W20#|2a?-+w)z$e5a9J=5}e9N{RYU+_uB-7HOO2j9n&3p!fn+{pgz zZ}}NpS3>vPUL$GX?URoNi z1ImgMNOH)_srPt!K6Pf#7 ze=$St{`}7@!0}u3p>a7J-t#x_1|$e4``;w%B_5eTnAu|W`tE#_depI~h)=MMJ+nw$ zDO;o}zr2UBUc@swEpJ^m-nDonr4}9pDw&W4CHVe+x(7Wx+{$D)5&yrr(NCQ(^9B~B za$hQI8ae{5EEW9PR~JQ}GroM`81Zoj(|V9dG6hEg6d#DK=A=8$^V*pQ#@r(reNcF* zTEeYf+twuP_ty30<%kw}O4-Zy5X8{w2Xx2@uKfsHS(E}h@aBf_{q(25J2r4B6Ip7T zpA@NXS^y_0k@goLOG$jTz1W)KsH-n~+}-`vdUN!1U|{5%psJ^GpanJOfUTqqmDgqpNCXT)_ls#JrB(o%8crU}m`7 zoZZ5t4^O(*EsQL$T8CgpUw~qzO0Tjj4bNlgz z%YL=U#e~*<+392!_>UeGCKdONEyI!OzKX&vKEl2r4@7%Yxo$Zb#OSKXxVU86&Q(UM z!PRwl`ac$k1j6==u0mbh(OEey6$~s7amzq|#u!T2z#W!NTv9w3Nr@*Dl?h@*##cU( zS3!k(2k~Ay>7-%QYaNwTfZ*>IFc!`B1&5~~Gc#KF$D!h`9uIyn^_Dw#JyzAy=NXLF zUL|z~)E~3P55J#AvjAeDzea+%)z|y*_>e;ql#nkY093;;k;yCju+qpbRNBoyG~`Y7 zcKLUQZ@PHMpRStC-NSMX9mXq%5)DS&OGd${qe;q8U>wW|3}MKWnc72&?ifTD& zJrO z%h{L;gb`7B)0wlV>fO8Uf)uB2X4gpSdQ<6I>tY#**g-~f$Y_lB6jp|_t)S){PZw+h zq%~mn{CIzaE8`N}WmEwIC%7G$5WgS1d3<`GV_^p_vL~0xR+|}1zs;h4nh`L?w#fbZ z#wcPDxb8L7)Or)BxVUX+F_>bt_%RH&PER#!9M@`7D2LK!A-RrEw&Z1?=FmOB20^{u z`(l?l#7?88j=>nR3&{cE)T%EmgbM4bN0M_GcvMg6x0X57d+VCNhV#8`S~Ws-0aT$D9z5BR3nRqPn=ms7#4!R*?`#x zD2C6-x&q?WP3__+T$)Z^>Q;Z!l~|4E++6*oN1=ji&CEz?Njy!G)m2dc6|e=PL<7-Q zT%J#PvmTRhx*ne!!wuWf)Wid7qmPd~Gkty83$-cv0v5kqr%wvCUzn|AwTh(~H#jyN zBVpi`(@z9VdcFCuca=JV$|i0Rvk;6k3*MbARoN}T-4L^UsK@#3dwZ=50KLg6DTjTH zO+4ES5PaL-7ZW+J4@-I(d!sp;Kt1s9>GgXJ@Nt$ykO+1Q{m{2!-1J5~o=i7a5KR0Q zrl)aoD^BLCGG_^?Trn=OAR%MWNQ4OR@dueHOm%OSC;Uc~p5boR+XJ#6WW9riuOQli z-zjH{w|#6>QBKbL;@2CHdc$a_M=FXn2zAORn2K?F9|O#MVA$;y*U*6Nuk?DIG`p#C z>y^s)6($X%JOv8!8U#Q){%)0S9J8wqiq7EnRg!Zqi;wr(l~3{Wr^Eb!PAsxjnmbMr zeSr7Wn~;DD?0tY0ql??*dXzl8SOgx&{K0b`-by2FP=Z^EIB#rhN=CA>jMkOEF|sr) z?P1Lp^vL12Lt_z#hb+UVB$~!9eg77h&43_2VRP{jz!uI{nqPrf18hR|YbhY=TK(?j z4OZ!2)8N?^TG{#U8I}XV$`?q>J^w7bfz|c6WLAu zIA##}3XYd8o}XA``ND41ms?-)G0uMed1HJ~QnBKqzA7_L3wcI*g9Q|!fZF7*V~v}# z4uw;m(hJ-xEYqrQUHrzkT8@S9R#njXs z$%K7kdwVASy0wpIaoYt=1>vz~(9_eKMf_VZ=eWer&dSGDcqGcRqV|g_+W1!7+}s?9Shzpz!NBNC z{{|bI62ugACqG}xX#K(c9DNymy4%U(1z6})q>DwywVGF#zaHrb0OuUWFy}}>0X5pM zGSxyxg&aN$7g6yE33>Ux2(*+nTTu9hiOSV#g4(O!uyj5n1H6WVxS1-oN`i1ds6F%1#VJ!RAx!l5sHhxx=*#n-B=9&j zh|$qt`Ln$N&JOx;h5+Pq)Co4N`#t4$_z>duN9{L8y{`3F$H!J{klp)5`7l_q@8O0$ z^RnPY3u*H+no@bP#A2=Og%GNm{q>)sw`BqPS$b(~4AyRh=AaEsrSiUhZxoS5$K0#{ z&`=C`0lA{?EJ*Gz-%c19an;x=pEjQ(B|g5;qnOWFvR*=P zB1zqQo&7d!^d25bF^O*opm7~v52S3y1UoV~dgb&7wnurMiQ-NR&`t?AIjl*1OI3g( z5Mg(=0lc>#T;0%p;Tb$0Oi)z!9Fw~;@<<}60rUcpr@3j#$vH0@pm|Pfz1Vgb8s;kSE&61dYP?KBEp+4*PCx9qM;2|6Nz$RaX@DszbgAHvWW}zK& zp76gG>?nFYU;8Ixne-aVFtA3tGlssOk$w25qsd}U=%f&V{ZniF_k`K>Ew|WSk>sFK zeI3&>($(3?NfZN8HxXuNp_N;(A~)s0`?lJ*ghWKn%#wLQlI)h(*0I(=`$Oe{ z#R&pH}`5CDsI-XnP2*6_*4b&d;+*zXl_QocTZ7KFJNsT9cFTvX}P3#19ck*q7sN>H91&k8rGoPEV0|QkemgQ{Z;PMTHe8m9eSdc{>cTRf z{>1bz8U@_%{_kqMBRA)4zZv2v(Euxi2=_Z06&M+zte0w(C!%255>=rl@H6UOE?|Vw zZIp6}g3>Zkk--8&SH3GZ?zxl&5+%$Aa+1&Wy;?39cW|9uF;=2_iqpFqI#h`zyJXRb7P-v@nP*?3R~OQ1f=H%X8#N4Z2Nn1 zI$i7*Pquc!UYo6!6C8^d<7yPrOcRg@(LDQ*Z+SUoZVv&X(eN{izNr`!>5X z=K~n102zL$x}quSANed{(fU52{&yISBRbZ8|1%2!c*EpZfo|x5Pvyx>nzlW?;X2uW zM*JeJI{fZG0iTnITwoAy#N?Ohi`!e9rTPJ5b!=gOVUo6FVn*ptc+!G)WfhXLHpHz) z3T*F$-B@vcz4*%lxbyVP47mG##*sQW2!HY>6@gSu3$&si>>U~5GY4_GQopzm-)8Uz zb_S3Gz0DD(W)k#jJ#9`VYeo>b?q#c^X(Avt4=#YArmeLl*eZjjyKk}GMge@UhK;4F z^t;$8#EvMCs9qt(A&u7GwbeV(N~>2-fDYbuO<0LGzZ;Z_KrKeINxP6!fR&ODmNNUz z1=f=+?n6UEpuYxK-+NG?QRvWXd{fH@O9iMTF9Z)@IVP}%MWo!Yd`5WI)i;$w00xA zcFdNm{a!5LXCm&cRuSV(C^t7QK36a&vazu#uo|cLdv^&Bmk9gCNTTR)A|EO=M2=AN zHH)@X4ewmX%+z#Ja1T!ln@+LF<}r>mv-xSH6P;W{Z9xTJ!!j%|_5Em0CL_JsX#q{5A?c|b+*ka9XEZH)z`9RBjNV&Q zW6**X7n9i*S$U3JN+Y#*CJrR5eG^Pf-@e#U4e|XwTEZq98uj~Id@8O51}qG8+W7bm z4y?2A!wFMnKTAqpelnGLJT%(RVa*H)6$v6(A>(VwL>cqjg8FeW2berHzG-|(CX##+ z5b{2+)u}7!@NwzS$rZ6^8GU{v++y%nz9i%{XJ4>17k5eVv|pFsY6LAVsE5Q?*pN}a z0W}JsHm5QfW@J1$ZO>P&SnlD$lz^jR#6v?~ek74rVe@>nIh`GD3lF0f!K7TXVK0tU z5RF_(tU@tY1Xl^KF9ivIer}GG!%yc+fpi%q^YO`!&Ug9L(=cVcH@ef!PUJw(lF;V7 z*()RSOfKr9C`4ZsaJqbR;dzV8tQi(l>jt)uefto7q{~*%i_wU*80|Dv5;;mrZ2qVZ zYtNVHFi43X6Q=Q_kVK%Y=xD(~xhe_&7vM+>>&u5lgA796Y&k;ry@+OyKdkivo9a|7 zgK0lHe@)_hvbwS;vLAP_rTu##2`?rt8tiJA3(QoNu@k;GFLb@gJpl${RBMJ_G%!Ah zY3J*wW8~J!rx;;>w_ofrF|DQxrh`1dR91FM6eSgO$KZBVzN81USf?3*V=Ht7XAOSFU|O=^HScFUL`*l-X3lfO?Y? z3Js-zl23Np;`fmvBiY>U{8Jw2^T3AIZl#H}YN5uES-m87Q4Cm`;85Rw)B4UNX`KkI z@h@TOhb*mgI*)_=XQH{opnWpDCM<3#GAFS8KZ%sj18J2pPK zw*V&jpQhv?@aA&&fMdHXm){JK+tnErGkHQA3O6h#a`?T@?9qDtA#9R3mhjnMUas(2 z&LuG?)uEcMX#-@0^JfB2s!B`Od0kKSY@#%}|DxzLdT&r9$>TD^!Ln88UioI^O(hP* z#KNHva7@RZZVD9_7q_&wCTY2*S)(&`w;LbYE!5zuYO(2;uvoo*J7IsP^;GBdI!P&+ z78d>@08)3NcTnn-)uaM|!ZGb61#`3?GM)F~Mr<7MF%|mpIl`cRS*i9He4{)8 zi;U-axLgc|hCeX>X~G=QAXy^8jKlHxnvg4wOt{JCz4`-@1S;Kj=~efFLmWJCDaAH>W;4O zkt8FlkYq!7ZI;7aDT^h}9IG?8Oj};TW+pymjsYuh^@<_@kX2=n%f6LieWvR6R)aAG z?SL3_LOPZgXyGq$8v}8;V_+&6`)DUC(`yHj2fw*RfGr@ofdT_XVGDuimhYV*Z zU+(Vd&a4I?2KC~Jqze!oIISnUKi5rxGyy3OV6BE?ttARrU*ybaI*?oGG2F(vNiX@#$!}q?#;2>8?4zIb$kO z&W)~lJ82EEM^e#buS+o!k{wFkx*JB!$Xiua^+UpGF)w%)e&6maYboGrKY;0)F(xh? zDcBi}0rjgd?Cbi@$@J674rmx!Nd-f!5>S+Y#u*FeM{;t=K_6UYWo487{aV);$z)eo zf37u*tSmgi;eKu)-5*GLWRXproE$PIu65G@9cf8PNsJD;se&EFWMnHu7M%f2E7kEA z%>X)`qQG?#|AKj$v-M_pGnrA=GjJp#;@okQZ(wv_!WN1i@Q)(%Y{!|ZBx>C=N zwhLQ@PET{-Ii0<8I#A6=x1!%jFgTyUFt*sA7QT3-Z6RxD2%R$4nC47(e;gFl-E7$E zN$1xe0|gQjhwm1^j+wHS%Sy2gTw6=@Hk7flV;S6qyp?@6lHz^DKo9}+9U4VZoc@3@ z^LV>-{bcn%;oEmJy!};SgVKIiUIE!{lz+CrB9q&WuBJa+rOUV3(S%v@Xu+vl z4}w!dPFx&956wRWCq{cVre>lDSe3rC{GYsB}hnjH%Pa1DcvH{ zN_Tflw<1!~0+JF^lF}du2+|>W7mx4zjNiTE-f{o*3}omz`|Q2GYtH$ZLY^BPz89}h zq@@Ca@78Wj*5f{GcR0J{;z&X;OTeN_EkN?JR|?Bgd`4iS1zBh2sG!FCiDw4?N9pLA zpsUHm1`jJ^y*D3#OM%0pPUYi-rYhng-L!{1JZG3m!5}DD3ffw^fEWKF>s$0KZo7>H zaOtPw8W7a7CD~J3;w8QYZvdxoqueTCGD`#N=2|2pjw#xi#zGfL(3PgD~I6rI1 zQ0+Zts0^SAx`Psf$^F<&sTC<~zLHqehql3KT>(uSd67Dr(lqWKLW?8d0LyQh`CPPq zv%Ft}i4($H@Q1$IZW>iNa-Z97L{g!xp11VAgUDmDq$yEx$r?)2cQ{XNTXRB^TKT@m zWR~8(lBK`FI-e``azH7b(Fa|iao1Ax&Om^^Te$>(InMwQQC&jDej4uDQ)}ZHS*>J0 zeTEa{t)MAFS5Cfj;T!R<%(CV;HcKC7%f@$^?u*sAt=!5gCs(DaMbt%nv~m(J=Y-ir zyL*f4DN@JlL(sBZn36U#Z&2bT)vn^js zq{usHX(NmK4;e==98m^tT2b(PN3{h$S+o|Y-Y)~Qfk%%ac6G2Zrk>#5Yt3hf7<609 zrxN+*y&n=)lsY%FWnAUotBJXAuOjlvCovjlBSSao=cD%LBiQxg;O})j(`>rD&5Dqm zm-uwX`?C(7^og?Zf{~YHp!-QOuJ#MT|Cj>?pNWEh;Qkz5-hX6&Z$9u3EE*|2?cDv( zDv?iSMH-;60a@}ZW*x2{nst9@Vx!ncVJhn zp-_75FhIw^GX;-P`wWr|&^}8jN|9BdVIAwOi6A+27PS}18T6;OiU3ms4 zBqU@FX2g%L;AO9QH%)^AQHhED&`zRe=k)Lw3s;ZJnKz?_xw&7p_X*7v`R=|e;|0%R z(?Tj5_Fd-vb(~!bsITB&!3F#KrRUi_uUn=fr76b&baJ4Y7WCZ+{0)T;@3-6<$7~+F zBu(tjWHD%9jB0%e;5WMa~xpoc#84=unG)w z@bf<=`1#lIkfL85Mk{mj$P=m-yUd5=jK3sj0yD|9*0t)&??st_h})WjWQTRgtHbYH zXwtMvH8o`MH2d$%`ajeY9xPa9P&7Tuy!^(s|1vktXnRyfFI+e@^(mVkHwdSRaI(U#j7;+a} zxLdgx{pmlYmDg{M%0SGG+jZ{s6sCG%kVlE~9Lj*1iH$4tcn)ttsSC`)IJM!u=Ylz% zO`D$V1gQRoV7G!9JqSVwIu#d>$iqI0Mp48w-jKpoE=jBmc-f27`e{K7kOK^oVjgV< zYJ)8J=-wrZCVz{yUBV?Q?Wk(M04qh!=XOZ62L z2OXYPOeD=;(JSs+a}Loh%c3HqZZjS{#AWyFQW$T5L1NItTZ#A0Q_D&3jp}tcb+*71 z3eJE==k3rr40PShv@~?(*Up3MSY8+XsvrvEb}NgjM$ufF6L7_SH`CIq>8N5`X$#b` zIasS0&+C5r=#7Cs?7a^Q+Iz?H!V4sbk4R!t5(0|lx$eH3_;NV7e0a1a0PRF&dn<$W z)i%>EU(-TH8%sdRp02jST{>pwz)if1`^n4Ti7D)OJASk@y{30yHiZ0LS3eMT2tO=^ z76vs#E@o#hZM49~^+%F;bfyRmHFek>LEQgZ8912j5tXH1=-;BT?7SWVcG`I7$U2qMS8P=b!4KEw>HM^x<@_e%yocjc4(EHDA z^l~Lb)r*vEEUiaN^nOCAXK!z>r84h(=#6I33M%lv{PoS2=+n#dj!M>_r;Fa^pj+0N@(DG=mY&HGazCyOH~9CVJJ?8Q;X~bk^O3hwK~j?DPS}dMIh85{3?0B5KP~w5 z`FltL)>j2SQc_sIKr#o3 z5>u6IUR85#w^%Q(If;-C<6xW+ab9Qgi0J~p`s?ORdB*1-a~q&Ivf{BEKI*pZND zq1=5f;@x|4R(hx*_zjU?uGH~#)atRUD5t1!l}zk|zb28k_uxJlgwK2O3LGM~i?R7s zCqi{b3y2xQCaa}1k?H+e+sYUf7TcNdaLg3(&ivX+HoBI;)X41^IV?)0%}?znwHIre zCJsyQgOgn_!q9~ag^!`#;q>b<%YkEGuB>3_J#rxr&KJ2zK^>OaFHSF zi)5to!zRayWBkLk579s>M>PA7JPRe>v;9H0>Mtl|T@Y#22!E&cWlFJzhEONb?y@@x zOcXTke%Z3$f6k6RX*z*s&(>WvvjK7V5dQfR=+|ydyAmCdP&hKmtS@!SQ|dw7c0CBC zrn=niU}tz^EOzXzrhm&b{mi~3l<^V+w8Fteaii~5*}qabEK+q4DqND-=yWP=j^+=p zvj)>V&jBbXV0$tYYffNl)!)xlyFJP0I>0z%#};5AJg77XVW|%24?cN;y43p)xebE_ zU0WNvs|YP{>fJU5tU&sSbkV{6@(s-nGsic>lK_%sumsoXGRh#7onjiq1)RuoQ4i63?1co@~llUh8 zwK@R8TA*mKIwi0DSopT z1hwU^C-Y&33J=kMO{2`tp2lgu$nJT{<5>(cH_Ix&NdOnaL z(qeys`XIW286GDyqPOHlWI^J!J&GC(vG8u$_t5y4KziA^JBUXr76 zr<_?*m-6#x$nYqouo`aaY~bSKqfGR)fz2p?JR%}O0u2kiO#Z~!AtE_tIhWiV`v&&O z79(`^H%u$D)+3aa^gB(`D7;ee^*ryYvo(swPn4BnwcXp zr^4EgbP`wk5Pjt^o#*t_!!6dV`OF?9)NEOP(?K$!C{iKz-d>!jG2@1*LYYNsxKeR&#_VCutt0CVr;zLt4tIF*?S>l z6OX0fQN7rHhDBEb*Co{$9U~T2ZS8Xoqsu*X=6sX0b zMeW_N=VN1MANlqxveTHitQVv{)bz9v_mHLC{5z!l5vqa?3lzuL#i+v_xT&{pVO}(^ z9>bG+kxOf6eEeu{PcZJyrht3cAocV7JJ`BN1rqMx-NPs;tcn4}u@dyH_$<&(mK!lm$MInaNG8XVwmjr@L5w%y#v{;KK5_FT)^$s$!EY$iBI$3VrLg+YmjCmJ|p%vk87BclP4 zs&xm*U@F7fuC|;Lq+h=&-6c(V`Z-_h_{&FC?wV!rFEcGmdh7UL5;eNZH|4cUj`Ap4 zVymPjB|RqoB9ghzCI^478T2(Cea%ukSD|u!Ui0;47cV}J9s*bMwx&kkb zjItGC0aZ|3Yoo}=tw8CY? zPK$lB2XN!gvOTW{=CUxKXpWopZA9E>n;FVxi9$WX(&_|1LPN@8orokV-qF^43y4>8 ziI40;=TE<-a*W=j@M;rle}K+Mn8=r9npB?rtx!qq0?*cuUx?S*O|v&Kn6b%!*D9$Rfhc@wGogq&vwv zII`RbGQN=uQp7bot+NmC%GJYM&>jRK$`?sakV7j#N@ijs;ZvK`&1r+XfH>hzTAHbQ zE%+j?Jxc}mr+S8Wzr>n0-omZ;OMz(a!QG?8E5>`U$7S>2`E0{%zE!-qTp)m~g3Gc* z1wGE)gbCHu8C13Te#O76s2~Xxr_-#S{6?!RMf^+N^eMNa^ z9fkg(zDHo2nwt9OWH=AxptWN*if^oN)!$@t9?+q%cm)=4IN3d&{QQ}0ELgQ?O0ldE zyb@E2{wPlB;G|_yKJHC?6njymAd0SQ*68{1_0|U0L2Q^D+b}LuL_}!iK|U(&(#kt6 zYPzWVe13DuX)=M)z<77t#lc8;B;`w8w@H{?-2ZA&tIUv;-HC{dK**Q-{WL`b48I~T zS25x~6c%z%O`s)-D^^<$pZ6xdgCYhC6D}LCXzu$0=G9QU<4KglgN?1Nt@}0OXK{ymjUE=>fea;^);ib*TdFGZ#B8h+`$>Xoy~yub`@O zB9xte)vKME5u`(+`pWpFHOyYY?ZlxCu(zS-G1yZFbYT()can>+;XffDYsY6JTG2nsZBbLzLouVIjf4#*P0S+YoA=5^!N@W%J$; zH+1B#s22_y_D0Is=W^YixPyRXxIv0|o+|J=_)-dgf)9*2&8H>WknStEIw<~>K0w^X zyqU^oLcp8Qs3dK6usQ6n`U85|kC7+?6BFSr@~;%|IGAihxrEH%(x^gbD0i3LTb(MJ zUXRm*5)f4N7Pgg%caGDep?-W1!|?8t8rvy2E8?IK(}{WQ=oiO~z+5aXZ4tN0q+3IQ zsJ*uDJ*2gu^Q1IX|JU!uhy6LlJ%4`KN9)H&Zt-~*E8!6}zb1PF3)NbzQ-|O5|5prI? zJ|BlOSgVLrlBgWBESGn&N^3t-F!)XajE0CS>*?pT>>t-S$?k7Ynd5q6gW!fdV>5XSF|4QAv_nZ2-lkLTB2!2919U3y@ zDt-&rJmM@R>Z+%U6Pj)HGf%v}(91WZDY|wTaE5Z4voG z*aP;R4^X6&6!!Efh{>?+SqO2UA)=ME`PEP6{CaPZjImDYC0P~0*C$_2PTZ*S5aCSv zQUl=-m>7wSI&s)Q-uwpHVK@S$aQ5`|^dM%47i;~c6ZHc7jxauZeoU?yNK45Qw@z|2 zf%IVaTFB+Wsf_Y#ZOgFdQNQGF#8hu(>o7?4i@w4%I@0lR!-T>%&w0 zKk*?UW6+QpblTNbsP7zhrbmXoE^7(5Q)<{-Ij^14f`ZWUg$gTc zOXxby?f6Nq81gWm_U@yf*27K2d~g1eJ0u-%Wg(SCh3N)|Q#dD~+oA`Tri5MharY+C zhpVKd>a~_xs>$EpzeD=H9YgDbp~UPmW{8ly$-WYEsC|KaBC3h!*}*e1RsRRvtZO89 zo5Qq|?%|~5r|-DY{X`6*+Qm_#F}1zB3Pq8Lm2L$( zO(6~T-G$yqTwIvd^AP%_U$4z-i*G0D2>eR@!9buE=9E&icS+9}w0xgo-?=imnfmBC zj0uRDa|*hg!k5>LdeYIly5?4yyH85a3?>AALdAu87aVzYqSq8B>&0&ioW`xU{iD#^zC_f$VqX{L~O)cPzQpe>OVYTixIZoUpB$C$)ZapP-THp9{*886E{>Ag?a#UUOCI|OszyK5BC;P2 zBC;FG8-=-V!yXmg@`?JI&P9cKxOe1>KHB7cMHVkwE_31Qj|L+!CYddI+u`Bi>eqb< zoo{Gft!I252PwggB)XHmBaUX;+9e|ek0Us3W*fL<-<~fJdxr6S7G}#q<|S~`ZHjyJ zxdO7l*Z!CgGXdujru-)%)YSY>sFNqLoMM}7gT5ffSz0~2T>5i+Z0RjB|A3NGqwChr zfy493@*FUepj!QFCW*J${$3D1vmqrT%SN9@endn+;CJ0M_gQ7SQoYBzgR@q zsnFxKR!ryObKk%tzQHCzHt6?^EF10M`W#wsqL=hmjpd?%>ZARv^|_m#Q|kTb-XmpM zSvW!b3BnwP3{85v-kd^x>h1|!wFupQbf`?Ja_UkeJ10@HW`tCOu}SXY)7?jzQVEH& z$nD>7h|)1|6#q8R^vwLfL zrM;AnGzZ$SrKUU|qj^kAUD9Q;W06FMRzlsA-Nbt2+ z(1Gz^l*3x5py>6}zq%5FlQjy$!(M)^tciTSDHttGMH|T;mG`;NV8_SnqAgdFd2PIb z-ADA>oHslU3a(hn9f%9P`_SpU8~IR7vJA9fz)n!1r)-x$orxD9sMi4iO|E2 zmsnC_6_V)q%=^5X`JBuPLz5t+)2M3i-R(O)w zCp)x?ZFWssp50mwonNOyD}L*;B>>7OkL9F~L{BA4ideKdN6&O{CBja7CKD5E!)A7O zht2HEn1zL(&LUB5Y;DP2`r3wWK0+qO;XB%#b6x_r-}Dj$K_(!K#_qfsMlDeNdwy_7 z#*v+{S}E$EBb0&H#i?_?-_5iOTzl9X>YkIJ5EAmZaJlsJ)^vD5QjRtW_nyV~YGNFj z102$hmjE|Iz2Uf@ZP-HjsYBOM`HhDO?lPbz7R|t%_>!Mrv~uhn)dDt;YxK3T5f!R9 z1|V5s<8>wUK6m=!Z2hSfut`-_ViR9aS zi{8E4zj$>_$v}wjcFW1_&z?qCJT0=RIh8bpWIrxZ9bVQwH81=@AV&j!qUY>I%cykCwQEU=5%PwRIx>UcJ+Jx3Mka-JddS=@KWO&p$h(|JswbA}eG^!{Mecd-#J; z@U*Sq6A67!BQ^eceonYxaX$IZ0={UxbqIL7u(xy)a51G6SD3l0ADrD<@MA>^ZO6pS z>>*nU9md)`d2?<%P=+n~-)I!sla~Gd_|g09E0JQQsFPGC@m&t!E=Wz$s1Ee^V_@h; zwHZ_hmcwo$7v~364aiY)sSZcR$7CpE9A`fW?jE$x4AT71}{Y&wRdA}mP7qu z@%64Ff|JBo8KFHy)yRVYW1nJ+Z0j-Ettf!DV8@A)LA|`lTsZiNfn}XkA0Bo6F0Y%gq>%rf=7TCkC&Y@lth4F2GxY*!k2bp zkXqZER7d*pbJNKLRRRfVGU3Jt9ET5Z zYclwJ%t}uG3QT^v_$V0(ZbaMjjuNH9pAeiN^2yM1WE+bD+`x<;DLeVLONbQT zl}eG4ohI!Iu}!cX)5ajR{$fS44ksPyu(iarB2g3D4Ay*rpX0NaoxGdvyQVoF7bK6e ziYNt}*iBk3ALRC6%!fJoWrp8@s=%LSpPiva=jUDS)MKnR=%lXB@Ht*{7_;%CvHOr{ z9^yupcV`ih7VLx57SITB`qgP30WyvemmPa;tcq42Ib2**wIokHNh&8J<&AKJpT!V+ ze>_hngJ$U$?aIIuhc$M)4ZI3Omnixh@i7Sg-2n(35-hx6F>!1a<>Lz+%eh@m!qqEh zD{;>+SwfV46)&;BCsQi2HM%%C^T`sKFRo;w0AC2RfCX4^`RGyk^e3Ovi#2nG+9KX} zwC-np!!qVC@wmdm=L>4s5Hb#^2TpxRDdXej`UgRWCfyN_ud<1Y?_TJ5_ADVND`V|| z@WLwd_*1}7cnthe*y}Pex%u|7ynOh$V75O=XN&qwGI#iHlx?xn^m9A3SqoBJ1>P{{@WhAB%GK|CTlHzJiyZC~FO!t0+pUeaP~ymm{_ zv8sq({}4)oLzo@I-(Tg;a&6Ao?)+fv^4GtS?PCo7jb`z>>ny~@VakVt)2ruR;EimA z>WveKDMEby=l)G6C9ppI><+xV8Z4dkz5eaz8ANn4tB5OHpy^{Nvm7iecSlP{aEiF? z#4tL7m+$o6!F1#EtbELN&A`UG2G5fnv6}QJu;hRA6^FEEs88gQ|@Gsbge@#e9 z)!0l#eWj(Lk=<9nj`kDqEWZfCW5%P8J5(&2cs^$v8ePIzPr8aJ_yY(T z3M(O>dccBBO6vKi7auqNbHZ0J(G?Xj(9=Ue(8gKPElc%5ojC+=*&!0>GPa(GD?6*MS zqyJkZQIu;|iE!0kBFf4_3f+R zoRsOM_6v$8lKuVbvp-tR@|KKgY>wmAIa4fvtqtnjhC7^YYTCtA+^VOqpM5OdO_ZJj z5ajW}`7G8DqBVP$)wf>$r_T*rl)f_eVg9jbe&kYceFO4FLj|Of+sO_kziKZPsHo(m z-*esf^Yf`JwH;|-pZg*n)SD1}5;GHl%ay(l`uLJ7Cmwh&y5@b2R;fKwgkJpySgoIg zIB(}?iQwTryvZZj#+1AF^hnn)4sGQFLCo#sW|iHvD=!~E5Rzvsv)Opi@5ji3S5yrg z1?ZJ31#&BQwx-o3aQgfD_Q43W{evE8auv^fYHPJVH8@ukCR~SjOVkQrV`gXD1?|IQ z5KJ$YOnj}(E(&~@L zLy0hI@4{Xii{szDw(Y=-I}~a|mlkho0R$Oe{_KaAC;?`U-#Z`)e<=_DF(Qd3jl=6V z0;hR8MV**PGRZ8#Ok!eU&Ad{bHm}@eSe$}*ps2IgL4S1=$o{b4nM80y9k0d*%}Ca^ zzP>#0s9b|CTVH^s;S-k^hi2qiC=8UH_7PPzHA^@1gERvJ8yXw!ryDXpuCdyzVi@0U zU{moqk=a}LTnLs^HBC*Ls_K8ElP_o8;jI9EB)h~kw;j!7B&`=a7nl1nb9^J(P#-p? zB4(m-CmnVL9iissj_~o>Yks=Fm;7y_6Qk7NmLU02BTxgOYf$mh6vkM!t-m`{^$KH@ z-?s>uceZEh$gcUM6T zh!o2T`!SZ#)9h?LamVOm+#j=TGL_!|E1;c`m6xAxau2VwrHE*?fUPzoLDY?A!MENL zP6{0xW7gKzY{L~RPQ6lPIs(A!xDNLPAqR8+lkOu0w6N7bJ7QQSXd?m^U0ABU$Bu51|H0WvE8E`0DWzMk5Sd1dHEjqvh3ry4{F-@ogEZ#Em_ z%Tx+Hb)FQfh^ec`7rbl(p(O0xTctOaSG(V&k^Gxe*vjgpb#y#DpuzW_j9Ji||081t zde*Xdfo1FisGd(i!&p-$fBJL4!203n#@PFGPvzLdzYEB%$;n8|m;4A&ZAFnJzD#>x zX=ppI*0d)#pdC#}a0c2ZUM_cG^5+-KzS=vmf)?lAjkRI|7O%Y@Jurd-834yd`W-Sn zbzbeSVy-imJXZY&@pMIjik{-GqN>59@irkFx3&&;KQ`YQB9m(eA*WMJof;)uto4EH z5V*gRByq1w49?IzJwLhcpXc}E2*EQ$e4?+7T#lIy$>;m*M;q7VG97}Di8l_rOAoID zOFAM9x%f=BT)mFtn|uK%92WhpU+$u!{6iWo%u{#yn_6<}!v`3Q;+}MV;`QPxr6R-|(4zai zF8Miy{_~-WVc@auBLqT=n~!*h&G`SmWJm@+9l4;}+S+g)?=o*E2;B0DpX9>|f$c-G zyaciRnFc$Oc9iqOcUDj8i;8%9vZ6@&L-04Mk)OZN9Njj-#wGo=vc9`B zoCoBWH!#y+L`~{F5Y@U|S}we%2fyyUsmAGf zI4~eM%40j}@Mo`ew$a7Gh_va!b^Vu>)iEGmz6`Rtg zS*raVDS6mbV*34q8a#U99&XPod&5T!0H?g%2SPKmU$*+w1+&iN)yHLZ?*w=ejf`~=vFPHm_@pxh^13MvN9QSGm>u@64V)BgUelW@-$hZM^}gU*9>4hUG0S#mCb z)u8W&|0Lz{&p^B@`fFgCHwh9-k039v0w)0Mf34y7`45?GY;5?sY-|(&aYj;+$8IEF zncUO9h#mlQ5eGNl>KurPDW^Q8<)q5E=EH3i(AGRVu(UyqyMZ$?5VsP;;IO6e+q>y~ zL$2D++f!Y0du_v0JXeZdXPrTBYy) ybN0A{bXA>vg@k}`b;apY&yIkAfO 100 { + pageSize = 20 + } + + qs := models.Orm.QueryTable(new(models.ErpAccountSet)). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Filter("delete_time__isnull", true) + + if keyword != "" { + qs = qs.Filter("set_name__contains", keyword) + } + if status != "" { + qs = qs.Filter("status", status) + } + + total, _ := qs.Count() + + var list []models.ErpAccountSet + _, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&list) + if err != nil { + c.asJsonErr(500, 500, "查询失败: "+err.Error()) + return + } + + c.asOk(map[string]interface{}{ + "list": list, + "total": total, + "page": page, + "pageSize": pageSize, + }) +} + +// Detail GET /backend/erp/account-set/:id +func (c *BackendErpAccountSetController) Detail() { + claims, err := c.asClaims() + if err != nil { + c.asJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.asJsonErr(400, 400, "无效的ID") + return + } + + var set models.ErpAccountSet + err = models.Orm.QueryTable(new(models.ErpAccountSet)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + One(&set) + if err != nil { + c.asJsonErr(404, 404, "账套未找到") + return + } + + c.asOk(set) +} + +// Create POST /backend/erp/account-set +func (c *BackendErpAccountSetController) Create() { + claims, err := c.asClaims() + if err != nil { + c.asJsonErr(401, 401, err.Error()) + return + } + + raw, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.asJsonErr(400, 400, "参数错误") + return + } + var p accountSetFormPayload + if err := json.Unmarshal(raw, &p); err != nil { + c.asJsonErr(400, 400, "参数错误") + return + } + + if strings.TrimSpace(p.SetName) == "" { + c.asJsonErr(400, 400, "账套名称不能为空") + return + } + if strings.TrimSpace(p.SetCode) == "" { + c.asJsonErr(400, 400, "账套编码不能为空") + return + } + + set := models.ErpAccountSet{ + TenantID: fmt.Sprintf("%d", claims.TenantId), + SetCode: strings.TrimSpace(p.SetCode), + SetName: strings.TrimSpace(p.SetName), + CompanyName: strings.TrimSpace(p.CompanyName), + CompanyTaxNo: strings.TrimSpace(p.CompanyTaxNo), + StartYear: p.StartYear, + StartMonth: p.StartMonth, + AccountingStandard: p.AccountingStandard, + Currency: strings.TrimSpace(p.Currency), + TaxRate: p.TaxRate, + Status: p.Status, + Remark: p.Remark, + CreateTime: time.Now(), + UpdateTime: time.Now(), + } + + if set.StartYear == 0 { + set.StartYear = time.Now().Year() + } + if set.StartMonth == 0 { + set.StartMonth = 1 + } + if set.AccountingStandard == "" { + set.AccountingStandard = "1" + } + if set.Currency == "" { + set.Currency = "CNY" + } + if set.TaxRate == 0 { + set.TaxRate = 13.00 + } + if set.Status == "" { + set.Status = "1" + } + + id, err := models.Orm.Insert(&set) + if err != nil { + c.asJsonErr(500, 500, "创建失败: "+err.Error()) + return + } + + c.asOk(map[string]interface{}{"id": id}) +} + +// Update PUT /backend/erp/account-set/:id +func (c *BackendErpAccountSetController) Update() { + claims, err := c.asClaims() + if err != nil { + c.asJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.asJsonErr(400, 400, "无效的ID") + return + } + + raw, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.asJsonErr(400, 400, "参数错误") + return + } + var p accountSetFormPayload + if err := json.Unmarshal(raw, &p); err != nil { + c.asJsonErr(400, 400, "参数错误") + return + } + + var set models.ErpAccountSet + err = models.Orm.QueryTable(new(models.ErpAccountSet)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + One(&set) + if err != nil { + c.asJsonErr(404, 404, "账套未找到") + return + } + + set.SetCode = strings.TrimSpace(p.SetCode) + set.SetName = strings.TrimSpace(p.SetName) + set.CompanyName = strings.TrimSpace(p.CompanyName) + set.CompanyTaxNo = strings.TrimSpace(p.CompanyTaxNo) + set.StartYear = p.StartYear + set.StartMonth = p.StartMonth + set.AccountingStandard = p.AccountingStandard + set.Currency = strings.TrimSpace(p.Currency) + set.TaxRate = p.TaxRate + set.Status = p.Status + set.Remark = p.Remark + set.UpdateTime = time.Now() + + _, err = models.Orm.Update(&set) + if err != nil { + c.asJsonErr(500, 500, "更新失败: "+err.Error()) + return + } + + c.asOk(nil) +} + +// Delete DELETE /backend/erp/account-set/:id +func (c *BackendErpAccountSetController) Delete() { + claims, err := c.asClaims() + if err != nil { + c.asJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.asJsonErr(400, 400, "无效的ID") + return + } + + now := time.Now() + _, err = models.Orm.QueryTable(new(models.ErpAccountSet)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Update(map[string]interface{}{ + "delete_time": now, + "update_time": now, + }) + if err != nil { + c.asJsonErr(500, 500, "删除失败: "+err.Error()) + return + } + + c.asOk(nil) +} + +// ToggleStatus POST /backend/erp/account-set/:id/status +func (c *BackendErpAccountSetController) ToggleStatus() { + claims, err := c.asClaims() + if err != nil { + c.asJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.asJsonErr(400, 400, "无效的ID") + return + } + + raw, _ := io.ReadAll(c.Ctx.Request.Body) + var p struct { + Status string `json:"status"` + } + _ = json.Unmarshal(raw, &p) + + _, err = models.Orm.QueryTable(new(models.ErpAccountSet)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Update(map[string]interface{}{ + "status": p.Status, + "update_time": time.Now(), + }) + if err != nil { + c.asJsonErr(500, 500, "操作失败: "+err.Error()) + return + } + + c.asOk(nil) +} diff --git a/go/controllers/backend_erp_company_contact.go b/go/controllers/backend_erp_company_contact.go new file mode 100644 index 0000000..5460c42 --- /dev/null +++ b/go/controllers/backend_erp_company_contact.go @@ -0,0 +1,263 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "io" + "strconv" + "strings" + "time" + + "server/models" + "server/pkg/jwtutil" + + beego "github.com/beego/beego/v2/server/web" +) + +// BackendErpCompanyContactController ERP客户/供应商联系人控制器 +type BackendErpCompanyContactController struct { + beego.Controller +} + +func (c *BackendErpCompanyContactController) contactClaims() (*jwtutil.Claims, error) { + auth := c.Ctx.Request.Header.Get("Authorization") + if auth == "" { + return nil, fmt.Errorf("未登录") + } + parts := strings.SplitN(auth, " ", 2) + if len(parts) != 2 || parts[0] != "Bearer" { + return nil, fmt.Errorf("认证信息格式错误") + } + claims, err := jwtutil.ParseToken(parts[1]) + if err != nil { + return nil, fmt.Errorf("无效的token") + } + if claims.UserType != "backend" { + return nil, fmt.Errorf("无权访问") + } + return claims, nil +} + +func (c *BackendErpCompanyContactController) contactJsonErr(httpStatus, bizCode int, msg string) { + c.Ctx.Output.SetStatus(httpStatus) + c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} + _ = c.ServeJSON() +} + +func (c *BackendErpCompanyContactController) contactOk(data interface{}) { + c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data} + _ = c.ServeJSON() +} + +type companyContactPayload struct { + CompanyType string `json:"company_type"` + CompanyID uint64 `json:"company_id"` + Name string `json:"name"` + Gender int8 `json:"gender"` + Phone string `json:"phone"` + Mobiles string `json:"mobiles"` // JSON数组字符串 + Wechat string `json:"wechat"` + QQ string `json:"qq"` + Dingtalk string `json:"dingtalk"` + Email string `json:"email"` + Department string `json:"department"` + Position string `json:"position"` + HomeAddress string `json:"home_address"` + IsPrimary int8 `json:"is_primary"` + Status int8 `json:"status"` + Remark string `json:"remark"` +} + +// List GET /backend/erp/company-contact/list?company_type=customer&company_id=1 +func (c *BackendErpCompanyContactController) List() { + claims, err := c.contactClaims() + if err != nil { + c.contactJsonErr(401, 401, err.Error()) + return + } + + companyType := strings.TrimSpace(c.GetString("company_type")) + companyIDStr := strings.TrimSpace(c.GetString("company_id")) + + if companyType == "" || companyIDStr == "" { + c.contactJsonErr(400, 400, "缺少公司参数") + return + } + + companyID, err := strconv.ParseUint(companyIDStr, 10, 64) + if err != nil { + c.contactJsonErr(400, 400, "公司ID格式错误") + return + } + + qs := models.Orm.QueryTable(new(models.ErpCompanyContact)). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Filter("company_type", companyType). + Filter("company_id", companyID). + Filter("delete_time__isnull", true) + + var list []models.ErpCompanyContact + _, err = qs.OrderBy("-is_primary", "id").All(&list) + if err != nil { + c.contactJsonErr(500, 500, "查询失败: "+err.Error()) + return + } + + c.contactOk(map[string]interface{}{"list": list}) +} + +// Create POST /backend/erp/company-contact +func (c *BackendErpCompanyContactController) Create() { + claims, err := c.contactClaims() + if err != nil { + c.contactJsonErr(401, 401, err.Error()) + return + } + + body, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.contactJsonErr(400, 400, "读取请求体失败") + return + } + var p companyContactPayload + if err := json.Unmarshal(body, &p); err != nil { + c.contactJsonErr(400, 400, "参数格式错误") + return + } + + if strings.TrimSpace(p.Name) == "" { + c.contactJsonErr(400, 400, "联系人姓名不能为空") + return + } + if p.CompanyType == "" || p.CompanyID == 0 { + c.contactJsonErr(400, 400, "缺少公司参数") + return + } + + contact := models.ErpCompanyContact{ + TenantID: fmt.Sprintf("%d", claims.TenantId), + CompanyType: p.CompanyType, + CompanyID: p.CompanyID, + Name: strings.TrimSpace(p.Name), + Gender: p.Gender, + Phone: strings.TrimSpace(p.Phone), + Mobiles: p.Mobiles, + Wechat: strings.TrimSpace(p.Wechat), + QQ: strings.TrimSpace(p.QQ), + Dingtalk: strings.TrimSpace(p.Dingtalk), + Email: strings.TrimSpace(p.Email), + Department: strings.TrimSpace(p.Department), + Position: strings.TrimSpace(p.Position), + HomeAddress: strings.TrimSpace(p.HomeAddress), + IsPrimary: p.IsPrimary, + Status: p.Status, + Remark: strings.TrimSpace(p.Remark), + CreateTime: time.Now(), + UpdateTime: time.Now(), + } + + id, err := models.Orm.Insert(&contact) + if err != nil { + c.contactJsonErr(500, 500, "创建失败: "+err.Error()) + return + } + + c.contactOk(map[string]interface{}{"id": id}) +} + +// Update PUT /backend/erp/company-contact/:id +func (c *BackendErpCompanyContactController) Update() { + claims, err := c.contactClaims() + if err != nil { + c.contactJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, err := strconv.ParseUint(idStr, 10, 64) + if err != nil { + c.contactJsonErr(400, 400, "ID格式错误") + return + } + + body, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.contactJsonErr(400, 400, "读取请求体失败") + return + } + var p companyContactPayload + if err := json.Unmarshal(body, &p); err != nil { + c.contactJsonErr(400, 400, "参数格式错误") + return + } + + contact := models.ErpCompanyContact{ID: id} + if err := models.Orm.Read(&contact); err != nil { + c.contactJsonErr(404, 404, "联系人不存在") + return + } + if contact.TenantID != fmt.Sprintf("%d", claims.TenantId) { + c.contactJsonErr(403, 403, "无权操作") + return + } + + contact.Name = strings.TrimSpace(p.Name) + contact.Gender = p.Gender + contact.Phone = strings.TrimSpace(p.Phone) + contact.Mobiles = p.Mobiles + contact.Wechat = strings.TrimSpace(p.Wechat) + contact.QQ = strings.TrimSpace(p.QQ) + contact.Dingtalk = strings.TrimSpace(p.Dingtalk) + contact.Email = strings.TrimSpace(p.Email) + contact.Department = strings.TrimSpace(p.Department) + contact.Position = strings.TrimSpace(p.Position) + contact.HomeAddress = strings.TrimSpace(p.HomeAddress) + contact.IsPrimary = p.IsPrimary + contact.Status = p.Status + contact.Remark = strings.TrimSpace(p.Remark) + contact.UpdateTime = time.Now() + + _, err = models.Orm.Update(&contact) + if err != nil { + c.contactJsonErr(500, 500, "更新失败: "+err.Error()) + return + } + + c.contactOk(nil) +} + +// Delete DELETE /backend/erp/company-contact/:id +func (c *BackendErpCompanyContactController) Delete() { + claims, err := c.contactClaims() + if err != nil { + c.contactJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, err := strconv.ParseUint(idStr, 10, 64) + if err != nil { + c.contactJsonErr(400, 400, "ID格式错误") + return + } + + contact := models.ErpCompanyContact{ID: id} + if err := models.Orm.Read(&contact); err != nil { + c.contactJsonErr(404, 404, "联系人不存在") + return + } + if contact.TenantID != fmt.Sprintf("%d", claims.TenantId) { + c.contactJsonErr(403, 403, "无权操作") + return + } + + now := time.Now() + contact.DeleteTime = &now + _, err = models.Orm.Update(&contact, "delete_time") + if err != nil { + c.contactJsonErr(500, 500, "删除失败: "+err.Error()) + return + } + + c.contactOk(nil) +} diff --git a/go/controllers/backend_erp_customer.go b/go/controllers/backend_erp_customer.go new file mode 100644 index 0000000..af3178a --- /dev/null +++ b/go/controllers/backend_erp_customer.go @@ -0,0 +1,419 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "io" + "strconv" + "strings" + "time" + + "server/models" + "server/pkg/jwtutil" + + beego "github.com/beego/beego/v2/server/web" +) + +// BackendErpCustomerController ERP客户管理控制器 +type BackendErpCustomerController struct { + beego.Controller +} + +func (c *BackendErpCustomerController) customerClaims() (*jwtutil.Claims, error) { + auth := c.Ctx.Request.Header.Get("Authorization") + if auth == "" { + return nil, fmt.Errorf("未登录") + } + parts := strings.SplitN(auth, " ", 2) + if len(parts) != 2 || parts[0] != "Bearer" { + return nil, fmt.Errorf("认证信息格式错误") + } + claims, err := jwtutil.ParseToken(parts[1]) + if err != nil { + return nil, fmt.Errorf("无效的token") + } + if claims.UserType != "backend" { + return nil, fmt.Errorf("无权访问") + } + return claims, nil +} + +func (c *BackendErpCustomerController) customerJsonErr(httpStatus, bizCode int, msg string) { + c.Ctx.Output.SetStatus(httpStatus) + c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} + _ = c.ServeJSON() +} + +func (c *BackendErpCustomerController) customerOk(data interface{}) { + c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data} + _ = c.ServeJSON() +} + +type customerFormPayload struct { + CustomerName string `json:"customer_name"` + CustomerType string `json:"customer_type"` + ContactPerson string `json:"contact_person"` + ContactPhone string `json:"contact_phone"` + ContactEmail string `json:"contact_email"` + CustomerLevel string `json:"customer_level"` + Industry string `json:"industry"` + RegisteredCapital string `json:"registered_capital"` + PaidCapital string `json:"paid_capital"` + EstablishDate string `json:"establish_date"` + AdministrativeDivision string `json:"administrative_division"` + EnterpriseType string `json:"enterprise_type"` + TaxpayerQualification string `json:"taxpayer_qualification"` + BusinessScope string `json:"business_scope"` + Address string `json:"address"` + RegisterTime string `json:"register_time"` + ExpireTime string `json:"expire_time"` + Status string `json:"status"` + IsDraft int8 `json:"is_draft"` + Remark string `json:"remark"` + InvoiceTitle string `json:"invoice_title"` + TaxNumber string `json:"tax_number"` + BankName string `json:"bank_name"` + BankAccount string `json:"bank_account"` + RegisteredAddress string `json:"registered_address"` + RegisteredPhone string `json:"registered_phone"` +} + +// List GET /backend/erp/customer/list +func (c *BackendErpCustomerController) List() { + claims, err := c.customerClaims() + if err != nil { + c.customerJsonErr(401, 401, err.Error()) + return + } + + page, _ := c.GetInt("page", 1) + pageSize, _ := c.GetInt("pageSize", 20) + keyword := strings.TrimSpace(c.GetString("keyword")) + customerType := strings.TrimSpace(c.GetString("customer_type")) + customerLevel := strings.TrimSpace(c.GetString("customer_level")) + status := strings.TrimSpace(c.GetString("status")) + isDraft := strings.TrimSpace(c.GetString("is_draft")) + + if page < 1 { + page = 1 + } + if pageSize < 1 || pageSize > 100 { + pageSize = 20 + } + + qs := models.Orm.QueryTable(new(models.TenantCrmCustomer)). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Filter("delete_time__isnull", true) + + if keyword != "" { + qs = qs.Filter("customer_name__contains", keyword) + } + if customerType != "" { + qs = qs.Filter("customer_type", customerType) + } + if customerLevel != "" { + qs = qs.Filter("customer_level", customerLevel) + } + if status != "" { + qs = qs.Filter("status", status) + } + if isDraft != "" { + if isDraft == "1" { + qs = qs.Filter("is_draft", 1) + } else { + qs = qs.Filter("is_draft", 0) + } + } + + total, _ := qs.Count() + + var list []models.TenantCrmCustomer + _, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&list) + if err != nil { + c.customerJsonErr(500, 500, "查询失败: "+err.Error()) + return + } + + c.customerOk(map[string]interface{}{ + "list": list, + "total": total, + "page": page, + "pageSize": pageSize, + }) +} + +// Detail GET /backend/erp/customer/:id +func (c *BackendErpCustomerController) Detail() { + claims, err := c.customerClaims() + if err != nil { + c.customerJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.customerJsonErr(400, 400, "无效的ID") + return + } + + var customer models.TenantCrmCustomer + err = models.Orm.QueryTable(new(models.TenantCrmCustomer)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + One(&customer) + if err != nil { + c.customerJsonErr(404, 404, "客户未找到") + return + } + + c.customerOk(customer) +} + +// Create POST /backend/erp/customer +func (c *BackendErpCustomerController) Create() { + claims, err := c.customerClaims() + if err != nil { + c.customerJsonErr(401, 401, err.Error()) + return + } + + raw, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.customerJsonErr(400, 400, "参数错误") + return + } + var p customerFormPayload + if err := json.Unmarshal(raw, &p); err != nil { + c.customerJsonErr(400, 400, "参数错误") + return + } + + if strings.TrimSpace(p.CustomerName) == "" { + c.customerJsonErr(400, 400, "客户名称不能为空") + return + } + // 正式保存时验证联系电话,草稿不强制 + if p.IsDraft == 0 && strings.TrimSpace(p.ContactPhone) == "" { + c.customerJsonErr(400, 400, "联系人电话不能为空") + return + } + + customer := models.TenantCrmCustomer{ + TenantID: fmt.Sprintf("%d", claims.TenantId), + CustomerName: strings.TrimSpace(p.CustomerName), + CustomerType: p.CustomerType, + ContactPerson: strings.TrimSpace(p.ContactPerson), + ContactPhone: strings.TrimSpace(p.ContactPhone), + ContactEmail: strings.TrimSpace(p.ContactEmail), + CustomerLevel: p.CustomerLevel, + Industry: strings.TrimSpace(p.Industry), + RegisteredCapital: strings.TrimSpace(p.RegisteredCapital), + PaidCapital: strings.TrimSpace(p.PaidCapital), + AdministrativeDivision: strings.TrimSpace(p.AdministrativeDivision), + EnterpriseType: strings.TrimSpace(p.EnterpriseType), + TaxpayerQualification: strings.TrimSpace(p.TaxpayerQualification), + BusinessScope: strings.TrimSpace(p.BusinessScope), + Address: strings.TrimSpace(p.Address), + Status: p.Status, + IsDraft: p.IsDraft, + Remark: p.Remark, + InvoiceTitle: strings.TrimSpace(p.InvoiceTitle), + TaxNumber: strings.TrimSpace(p.TaxNumber), + BankName: strings.TrimSpace(p.BankName), + BankAccount: strings.TrimSpace(p.BankAccount), + RegisteredAddress: strings.TrimSpace(p.RegisteredAddress), + RegisteredPhone: strings.TrimSpace(p.RegisteredPhone), + CreateTime: time.Now(), + UpdateTime: time.Now(), + } + + if p.RegisterTime != "" { + if t, err := time.ParseInLocation("2006-01-02", p.RegisterTime, time.Local); err == nil { + customer.RegisterTime = &t + } + } + if p.ExpireTime != "" { + if t, err := time.ParseInLocation("2006-01-02", p.ExpireTime, time.Local); err == nil { + customer.ExpireTime = &t + } + } + if p.EstablishDate != "" { + if t, err := time.ParseInLocation("2006-01-02", p.EstablishDate, time.Local); err == nil { + customer.EstablishDate = &t + } + } + if customer.CustomerLevel == "" { + customer.CustomerLevel = "3" + } + if customer.Status == "" { + customer.Status = "1" + } + + id, err := models.Orm.Insert(&customer) + if err != nil { + c.customerJsonErr(500, 500, "创建失败: "+err.Error()) + return + } + + c.customerOk(map[string]interface{}{"id": id}) +} + +// Update PUT /backend/erp/customer/:id +func (c *BackendErpCustomerController) Update() { + claims, err := c.customerClaims() + if err != nil { + c.customerJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.customerJsonErr(400, 400, "无效的ID") + return + } + + raw, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.customerJsonErr(400, 400, "参数错误") + return + } + var p customerFormPayload + if err := json.Unmarshal(raw, &p); err != nil { + c.customerJsonErr(400, 400, "参数错误") + return + } + + var customer models.TenantCrmCustomer + err = models.Orm.QueryTable(new(models.TenantCrmCustomer)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + One(&customer) + if err != nil { + c.customerJsonErr(404, 404, "客户未找到") + return + } + + customer.CustomerName = strings.TrimSpace(p.CustomerName) + customer.CustomerType = p.CustomerType + customer.ContactPerson = strings.TrimSpace(p.ContactPerson) + customer.ContactPhone = strings.TrimSpace(p.ContactPhone) + customer.ContactEmail = strings.TrimSpace(p.ContactEmail) + customer.CustomerLevel = p.CustomerLevel + customer.Industry = strings.TrimSpace(p.Industry) + customer.RegisteredCapital = strings.TrimSpace(p.RegisteredCapital) + customer.PaidCapital = strings.TrimSpace(p.PaidCapital) + customer.AdministrativeDivision = strings.TrimSpace(p.AdministrativeDivision) + customer.EnterpriseType = strings.TrimSpace(p.EnterpriseType) + customer.TaxpayerQualification = strings.TrimSpace(p.TaxpayerQualification) + customer.BusinessScope = strings.TrimSpace(p.BusinessScope) + customer.Address = strings.TrimSpace(p.Address) + customer.Status = p.Status + customer.IsDraft = p.IsDraft + customer.Remark = p.Remark + customer.InvoiceTitle = strings.TrimSpace(p.InvoiceTitle) + customer.TaxNumber = strings.TrimSpace(p.TaxNumber) + customer.BankName = strings.TrimSpace(p.BankName) + customer.BankAccount = strings.TrimSpace(p.BankAccount) + customer.RegisteredAddress = strings.TrimSpace(p.RegisteredAddress) + customer.RegisteredPhone = strings.TrimSpace(p.RegisteredPhone) + customer.UpdateTime = time.Now() + + if p.RegisterTime != "" { + if t, err := time.ParseInLocation("2006-01-02", p.RegisterTime, time.Local); err == nil { + customer.RegisterTime = &t + } + } else { + customer.RegisterTime = nil + } + if p.ExpireTime != "" { + if t, err := time.ParseInLocation("2006-01-02", p.ExpireTime, time.Local); err == nil { + customer.ExpireTime = &t + } + } else { + customer.ExpireTime = nil + } + if p.EstablishDate != "" { + if t, err := time.ParseInLocation("2006-01-02", p.EstablishDate, time.Local); err == nil { + customer.EstablishDate = &t + } + } else { + customer.EstablishDate = nil + } + + _, err = models.Orm.Update(&customer) + if err != nil { + c.customerJsonErr(500, 500, "更新失败: "+err.Error()) + return + } + + c.customerOk(nil) +} + +// Delete DELETE /backend/erp/customer/:id +func (c *BackendErpCustomerController) Delete() { + claims, err := c.customerClaims() + if err != nil { + c.customerJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.customerJsonErr(400, 400, "无效的ID") + return + } + + now := time.Now() + _, err = models.Orm.QueryTable(new(models.TenantCrmCustomer)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Update(map[string]interface{}{ + "delete_time": now, + "update_time": now, + }) + if err != nil { + c.customerJsonErr(500, 500, "删除失败: "+err.Error()) + return + } + + c.customerOk(nil) +} + +// ToggleStatus POST /backend/erp/customer/:id/status +func (c *BackendErpCustomerController) ToggleStatus() { + claims, err := c.customerClaims() + if err != nil { + c.customerJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.customerJsonErr(400, 400, "无效的ID") + return + } + + raw, _ := io.ReadAll(c.Ctx.Request.Body) + var p struct { + Status string `json:"status"` + } + _ = json.Unmarshal(raw, &p) + + _, err = models.Orm.QueryTable(new(models.TenantCrmCustomer)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Update(map[string]interface{}{ + "status": p.Status, + "update_time": time.Now(), + }) + if err != nil { + c.customerJsonErr(500, 500, "操作失败: "+err.Error()) + return + } + + c.customerOk(nil) +} diff --git a/go/controllers/backend_erp_normal_setting.go b/go/controllers/backend_erp_normal_setting.go new file mode 100644 index 0000000..b0ff56f --- /dev/null +++ b/go/controllers/backend_erp_normal_setting.go @@ -0,0 +1,288 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "io" + "strings" + "time" + + "server/models" + "server/pkg/jwtutil" + + beego "github.com/beego/beego/v2/server/web" +) + +// BackendErpNormalSettingController ERP基础设置控制器 +type BackendErpNormalSettingController struct { + beego.Controller +} + +func (c *BackendErpNormalSettingController) nsClaims() (*jwtutil.Claims, error) { + auth := c.Ctx.Request.Header.Get("Authorization") + if auth == "" { + return nil, fmt.Errorf("未登录") + } + parts := strings.SplitN(auth, " ", 2) + if len(parts) != 2 || parts[0] != "Bearer" { + return nil, fmt.Errorf("认证信息格式错误") + } + claims, err := jwtutil.ParseToken(parts[1]) + if err != nil { + return nil, fmt.Errorf("无效的token") + } + if claims.UserType != "backend" { + return nil, fmt.Errorf("无权访问") + } + return claims, nil +} + +func (c *BackendErpNormalSettingController) nsJsonErr(httpStatus, bizCode int, msg string) { + c.Ctx.Output.SetStatus(httpStatus) + c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} + _ = c.ServeJSON() +} + +func (c *BackendErpNormalSettingController) nsOk(data interface{}) { + c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data} + _ = c.ServeJSON() +} + +// 默认基础配置项定义 +var defaultErpNormalSettings = []struct { + Key string + Name string + Group string + Type string + Value string + Options string + SortOrder int +}{ + // 公司信息 + {"company_name", "公司名称", "company", "input", "", "", 1}, + {"company_address", "公司地址", "company", "input", "", "", 2}, + {"company_phone", "公司电话", "company", "input", "", "", 3}, + {"company_email", "公司邮箱", "company", "input", "", "", 4}, + {"company_fax", "公司传真", "company", "input", "", "", 5}, + {"company_website", "公司网址", "company", "input", "", "", 6}, + // 税务设置 + {"taxpayer_name", "纳税人名称", "tax", "input", "", "", 1}, + {"taxpayer_no", "纳税人识别号", "tax", "input", "", "", 2}, + {"default_tax_rate", "默认税率(%)", "tax", "number", "13", "", 3}, + {"tax_type", "纳税人类型", "tax", "select", "general", "[{\"label\":\"一般纳税人\",\"value\":\"general\"},{\"label\":\"小规模纳税人\",\"value\":\"small\"}]", 4}, + // 单据设置 + {"sale_order_prefix", "销售单号前缀", "document", "input", "XS", "", 1}, + {"purchase_order_prefix", "采购单号前缀", "document", "input", "CG", "", 2}, + {"stock_in_prefix", "入库单号前缀", "document", "input", "RK", "", 3}, + {"stock_out_prefix", "出库单号前缀", "document", "input", "CK", "", 4}, + {"return_order_prefix", "退货单号前缀", "document", "input", "TH", "", 5}, + {"auto_generate_doc_no", "自动生成单据号", "document", "switch", "1", "", 6}, + // 库存设置 + {"stock_warning_threshold", "库存预警阈值", "inventory", "number", "10", "", 1}, + {"default_warehouse", "默认仓库", "inventory", "input", "", "", 2}, + {"negative_stock_allowed", "允许负库存", "inventory", "switch", "0", "", 3}, + {"stock_check_cycle", "盘点周期(天)", "inventory", "number", "30", "", 4}, + // 财务设置 + {"default_currency", "默认币种", "finance", "select", "CNY", "[{\"label\":\"人民币(CNY)\",\"value\":\"CNY\"},{\"label\":\"美元(USD)\",\"value\":\"USD\"},{\"label\":\"欧元(EUR)\",\"value\":\"EUR\"}]", 1}, + {"default_payment_method", "默认付款方式", "finance", "select", "bank_transfer", "[{\"label\":\"银行转账\",\"value\":\"bank_transfer\"},{\"label\":\"现金\",\"value\":\"cash\"},{\"label\":\"支票\",\"value\":\"check\"},{\"label\":\"在线支付\",\"value\":\"online\"}]", 2}, + {"accounting_standard", "会计制度", "finance", "select", "enterprise", "[{\"label\":\"企业会计准则\",\"value\":\"enterprise\"},{\"label\":\"小企业会计准则\",\"value\":\"small_enterprise\"}]", 3}, + {"fiscal_year_start", "会计年度起始月", "finance", "number", "1", "", 4}, +} + +// List GET /backend/erp/normal-settings +// 获取租户所有基础设置,按分组返回;如果租户还没有配置,自动初始化默认配置 +func (c *BackendErpNormalSettingController) List() { + claims, err := c.nsClaims() + if err != nil { + c.nsJsonErr(401, 401, err.Error()) + return + } + + tenantID := fmt.Sprintf("%d", claims.TenantId) + + // 查询租户现有配置 + list, err := models.GetErpNormalSettingsByTenant(tenantID) + if err != nil { + c.nsJsonErr(500, 500, "查询失败: "+err.Error()) + return + } + + // 如果没有配置,自动初始化默认配置 + if len(list) == 0 { + now := time.Now() + for _, item := range defaultErpNormalSettings { + setting := models.ErpNormalSetting{ + TenantID: tenantID, + ConfigKey: item.Key, + ConfigValue: item.Value, + ConfigName: item.Name, + ConfigGroup: item.Group, + ConfigType: item.Type, + ConfigOptions: item.Options, + SortOrder: item.SortOrder, + Status: 1, + CreateTime: now, + UpdateTime: now, + } + _, _ = models.Orm.Insert(&setting) + } + // 重新查询 + list, _ = models.GetErpNormalSettingsByTenant(tenantID) + } + + // 按分组组织 + groups := make(map[string][]models.ErpNormalSetting) + var groupOrder []string + for _, item := range list { + if _, ok := groups[item.ConfigGroup]; !ok { + groupOrder = append(groupOrder, item.ConfigGroup) + } + groups[item.ConfigGroup] = append(groups[item.ConfigGroup], item) + } + + // 按固定顺序排列分组 + groupNameMap := map[string]string{ + "company": "公司信息", + "tax": "税务设置", + "document": "单据设置", + "inventory": "库存设置", + "finance": "财务设置", + } + + type groupResult struct { + Group string `json:"group"` + Name string `json:"name"` + Items []models.ErpNormalSetting `json:"items"` + } + + var result []groupResult + desiredOrder := []string{"company", "tax", "document", "inventory", "finance"} + for _, g := range desiredOrder { + if items, ok := groups[g]; ok { + result = append(result, groupResult{ + Group: g, + Name: groupNameMap[g], + Items: items, + }) + } + } + // 其他未在预设顺序中的分组 + for _, g := range groupOrder { + found := false + for _, d := range desiredOrder { + if g == d { + found = true + break + } + } + if !found { + result = append(result, groupResult{ + Group: g, + Name: groupNameMap[g], + Items: groups[g], + }) + } + } + + c.nsOk(result) +} + +// Save PUT /backend/erp/normal-settings +// 批量保存配置(接收 key-value 映射) +func (c *BackendErpNormalSettingController) Save() { + claims, err := c.nsClaims() + if err != nil { + c.nsJsonErr(401, 401, err.Error()) + return + } + + raw, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.nsJsonErr(400, 400, "参数错误") + return + } + + // 支持两种格式:{"key1":"value1","key2":"value2"} 或 [{"config_key":"xxx","config_value":"xxx"}] + var kvMap map[string]string + if err := json.Unmarshal(raw, &kvMap); err != nil { + // 尝试数组格式 + var arr []struct { + ConfigKey string `json:"config_key"` + ConfigValue string `json:"config_value"` + } + if err2 := json.Unmarshal(raw, &arr); err2 != nil { + c.nsJsonErr(400, 400, "参数格式错误") + return + } + kvMap = make(map[string]string) + for _, item := range arr { + kvMap[item.ConfigKey] = item.ConfigValue + } + } + + tenantID := fmt.Sprintf("%d", claims.TenantId) + now := time.Now() + + for key, value := range kvMap { + setting := models.GetErpNormalSettingByKey(tenantID, key) + if setting != nil { + setting.ConfigValue = value + setting.UpdateTime = now + _, _ = models.Orm.Update(setting, "ConfigValue", "UpdateTime") + } else { + // 不存在则创建 + newSetting := models.ErpNormalSetting{ + TenantID: tenantID, + ConfigKey: key, + ConfigValue: value, + ConfigName: key, + ConfigGroup: "custom", + ConfigType: "input", + Status: 1, + CreateTime: now, + UpdateTime: now, + } + _, _ = models.Orm.Insert(&newSetting) + } + } + + c.nsOk(nil) +} + +// Reset POST /backend/erp/normal-settings/reset +// 重置为默认配置 +func (c *BackendErpNormalSettingController) Reset() { + claims, err := c.nsClaims() + if err != nil { + c.nsJsonErr(401, 401, err.Error()) + return + } + + tenantID := fmt.Sprintf("%d", claims.TenantId) + + // 删除现有配置 + _, _ = models.Orm.QueryTable(new(models.ErpNormalSetting)). + Filter("tenant_id", tenantID). + Delete() + + // 重新初始化默认配置 + now := time.Now() + for _, item := range defaultErpNormalSettings { + setting := models.ErpNormalSetting{ + TenantID: tenantID, + ConfigKey: item.Key, + ConfigValue: item.Value, + ConfigName: item.Name, + ConfigGroup: item.Group, + ConfigType: item.Type, + ConfigOptions: item.Options, + SortOrder: item.SortOrder, + Status: 1, + CreateTime: now, + UpdateTime: now, + } + _, _ = models.Orm.Insert(&setting) + } + + c.nsOk(nil) +} diff --git a/go/controllers/backend_erp_supplier.go b/go/controllers/backend_erp_supplier.go new file mode 100644 index 0000000..7e17337 --- /dev/null +++ b/go/controllers/backend_erp_supplier.go @@ -0,0 +1,419 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "io" + "strconv" + "strings" + "time" + + "server/models" + "server/pkg/jwtutil" + + beego "github.com/beego/beego/v2/server/web" +) + +// BackendErpSupplierController ERP供应商管理控制器 +type BackendErpSupplierController struct { + beego.Controller +} + +func (c *BackendErpSupplierController) supplierClaims() (*jwtutil.Claims, error) { + auth := c.Ctx.Request.Header.Get("Authorization") + if auth == "" { + return nil, fmt.Errorf("未登录") + } + parts := strings.SplitN(auth, " ", 2) + if len(parts) != 2 || parts[0] != "Bearer" { + return nil, fmt.Errorf("认证信息格式错误") + } + claims, err := jwtutil.ParseToken(parts[1]) + if err != nil { + return nil, fmt.Errorf("无效的token") + } + if claims.UserType != "backend" { + return nil, fmt.Errorf("无权访问") + } + return claims, nil +} + +func (c *BackendErpSupplierController) supplierJsonErr(httpStatus, bizCode int, msg string) { + c.Ctx.Output.SetStatus(httpStatus) + c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} + _ = c.ServeJSON() +} + +func (c *BackendErpSupplierController) supplierOk(data interface{}) { + c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data} + _ = c.ServeJSON() +} + +type supplierFormPayload struct { + SupplierName string `json:"supplier_name"` + SupplierType string `json:"supplier_type"` + ContactPerson string `json:"contact_person"` + ContactPhone string `json:"contact_phone"` + ContactEmail string `json:"contact_email"` + SupplierLevel string `json:"supplier_level"` + Industry string `json:"industry"` + RegisteredCapital string `json:"registered_capital"` + PaidCapital string `json:"paid_capital"` + EstablishDate string `json:"establish_date"` + AdministrativeDivision string `json:"administrative_division"` + EnterpriseType string `json:"enterprise_type"` + TaxpayerQualification string `json:"taxpayer_qualification"` + BusinessScope string `json:"business_scope"` + Address string `json:"address"` + RegisterTime string `json:"register_time"` + ExpireTime string `json:"expire_time"` + Status string `json:"status"` + IsDraft int8 `json:"is_draft"` + Remark string `json:"remark"` + InvoiceTitle string `json:"invoice_title"` + TaxNumber string `json:"tax_number"` + BankName string `json:"bank_name"` + BankAccount string `json:"bank_account"` + RegisteredAddress string `json:"registered_address"` + RegisteredPhone string `json:"registered_phone"` +} + +// List GET /backend/erp/supplier/list +func (c *BackendErpSupplierController) List() { + claims, err := c.supplierClaims() + if err != nil { + c.supplierJsonErr(401, 401, err.Error()) + return + } + + page, _ := c.GetInt("page", 1) + pageSize, _ := c.GetInt("pageSize", 20) + keyword := strings.TrimSpace(c.GetString("keyword")) + supplierType := strings.TrimSpace(c.GetString("supplier_type")) + supplierLevel := strings.TrimSpace(c.GetString("supplier_level")) + status := strings.TrimSpace(c.GetString("status")) + isDraft := strings.TrimSpace(c.GetString("is_draft")) + + if page < 1 { + page = 1 + } + if pageSize < 1 || pageSize > 100 { + pageSize = 20 + } + + qs := models.Orm.QueryTable(new(models.TenantCrmSupplier)). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Filter("delete_time__isnull", true) + + if keyword != "" { + qs = qs.Filter("supplier_name__contains", keyword) + } + if supplierType != "" { + qs = qs.Filter("supplier_type", supplierType) + } + if supplierLevel != "" { + qs = qs.Filter("supplier_level", supplierLevel) + } + if status != "" { + qs = qs.Filter("status", status) + } + if isDraft != "" { + if isDraft == "1" { + qs = qs.Filter("is_draft", 1) + } else { + qs = qs.Filter("is_draft", 0) + } + } + + total, _ := qs.Count() + + var list []models.TenantCrmSupplier + _, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&list) + if err != nil { + c.supplierJsonErr(500, 500, "查询失败: "+err.Error()) + return + } + + c.supplierOk(map[string]interface{}{ + "list": list, + "total": total, + "page": page, + "pageSize": pageSize, + }) +} + +// Detail GET /backend/erp/supplier/:id +func (c *BackendErpSupplierController) Detail() { + claims, err := c.supplierClaims() + if err != nil { + c.supplierJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.supplierJsonErr(400, 400, "无效的ID") + return + } + + var supplier models.TenantCrmSupplier + err = models.Orm.QueryTable(new(models.TenantCrmSupplier)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + One(&supplier) + if err != nil { + c.supplierJsonErr(404, 404, "供应商未找到") + return + } + + c.supplierOk(supplier) +} + +// Create POST /backend/erp/supplier +func (c *BackendErpSupplierController) Create() { + claims, err := c.supplierClaims() + if err != nil { + c.supplierJsonErr(401, 401, err.Error()) + return + } + + raw, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.supplierJsonErr(400, 400, "参数错误") + return + } + var p supplierFormPayload + if err := json.Unmarshal(raw, &p); err != nil { + c.supplierJsonErr(400, 400, "参数错误") + return + } + + if strings.TrimSpace(p.SupplierName) == "" { + c.supplierJsonErr(400, 400, "供应商名称不能为空") + return + } + // 正式保存时验证联系电话,草稿不强制 + if p.IsDraft == 0 && strings.TrimSpace(p.ContactPhone) == "" { + c.supplierJsonErr(400, 400, "联系人电话不能为空") + return + } + + supplier := models.TenantCrmSupplier{ + TenantID: fmt.Sprintf("%d", claims.TenantId), + SupplierName: strings.TrimSpace(p.SupplierName), + SupplierType: p.SupplierType, + ContactPerson: strings.TrimSpace(p.ContactPerson), + ContactPhone: strings.TrimSpace(p.ContactPhone), + ContactEmail: strings.TrimSpace(p.ContactEmail), + SupplierLevel: p.SupplierLevel, + Industry: strings.TrimSpace(p.Industry), + RegisteredCapital: strings.TrimSpace(p.RegisteredCapital), + PaidCapital: strings.TrimSpace(p.PaidCapital), + AdministrativeDivision: strings.TrimSpace(p.AdministrativeDivision), + EnterpriseType: strings.TrimSpace(p.EnterpriseType), + TaxpayerQualification: strings.TrimSpace(p.TaxpayerQualification), + BusinessScope: strings.TrimSpace(p.BusinessScope), + Address: strings.TrimSpace(p.Address), + Status: p.Status, + IsDraft: p.IsDraft, + Remark: p.Remark, + InvoiceTitle: strings.TrimSpace(p.InvoiceTitle), + TaxNumber: strings.TrimSpace(p.TaxNumber), + BankName: strings.TrimSpace(p.BankName), + BankAccount: strings.TrimSpace(p.BankAccount), + RegisteredAddress: strings.TrimSpace(p.RegisteredAddress), + RegisteredPhone: strings.TrimSpace(p.RegisteredPhone), + CreateTime: time.Now(), + UpdateTime: time.Now(), + } + + if p.RegisterTime != "" { + if t, err := time.ParseInLocation("2006-01-02", p.RegisterTime, time.Local); err == nil { + supplier.RegisterTime = &t + } + } + if p.ExpireTime != "" { + if t, err := time.ParseInLocation("2006-01-02", p.ExpireTime, time.Local); err == nil { + supplier.ExpireTime = &t + } + } + if p.EstablishDate != "" { + if t, err := time.ParseInLocation("2006-01-02", p.EstablishDate, time.Local); err == nil { + supplier.EstablishDate = &t + } + } + if supplier.SupplierLevel == "" { + supplier.SupplierLevel = "3" + } + if supplier.Status == "" { + supplier.Status = "1" + } + + id, err := models.Orm.Insert(&supplier) + if err != nil { + c.supplierJsonErr(500, 500, "创建失败: "+err.Error()) + return + } + + c.supplierOk(map[string]interface{}{"id": id}) +} + +// Update PUT /backend/erp/supplier/:id +func (c *BackendErpSupplierController) Update() { + claims, err := c.supplierClaims() + if err != nil { + c.supplierJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.supplierJsonErr(400, 400, "无效的ID") + return + } + + raw, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.supplierJsonErr(400, 400, "参数错误") + return + } + var p supplierFormPayload + if err := json.Unmarshal(raw, &p); err != nil { + c.supplierJsonErr(400, 400, "参数错误") + return + } + + var supplier models.TenantCrmSupplier + err = models.Orm.QueryTable(new(models.TenantCrmSupplier)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + One(&supplier) + if err != nil { + c.supplierJsonErr(404, 404, "供应商未找到") + return + } + + supplier.SupplierName = strings.TrimSpace(p.SupplierName) + supplier.SupplierType = p.SupplierType + supplier.ContactPerson = strings.TrimSpace(p.ContactPerson) + supplier.ContactPhone = strings.TrimSpace(p.ContactPhone) + supplier.ContactEmail = strings.TrimSpace(p.ContactEmail) + supplier.SupplierLevel = p.SupplierLevel + supplier.Industry = strings.TrimSpace(p.Industry) + supplier.RegisteredCapital = strings.TrimSpace(p.RegisteredCapital) + supplier.PaidCapital = strings.TrimSpace(p.PaidCapital) + supplier.AdministrativeDivision = strings.TrimSpace(p.AdministrativeDivision) + supplier.EnterpriseType = strings.TrimSpace(p.EnterpriseType) + supplier.TaxpayerQualification = strings.TrimSpace(p.TaxpayerQualification) + supplier.BusinessScope = strings.TrimSpace(p.BusinessScope) + supplier.Address = strings.TrimSpace(p.Address) + supplier.Status = p.Status + supplier.IsDraft = p.IsDraft + supplier.Remark = p.Remark + supplier.InvoiceTitle = strings.TrimSpace(p.InvoiceTitle) + supplier.TaxNumber = strings.TrimSpace(p.TaxNumber) + supplier.BankName = strings.TrimSpace(p.BankName) + supplier.BankAccount = strings.TrimSpace(p.BankAccount) + supplier.RegisteredAddress = strings.TrimSpace(p.RegisteredAddress) + supplier.RegisteredPhone = strings.TrimSpace(p.RegisteredPhone) + supplier.UpdateTime = time.Now() + + if p.RegisterTime != "" { + if t, err := time.ParseInLocation("2006-01-02", p.RegisterTime, time.Local); err == nil { + supplier.RegisterTime = &t + } + } else { + supplier.RegisterTime = nil + } + if p.ExpireTime != "" { + if t, err := time.ParseInLocation("2006-01-02", p.ExpireTime, time.Local); err == nil { + supplier.ExpireTime = &t + } + } else { + supplier.ExpireTime = nil + } + if p.EstablishDate != "" { + if t, err := time.ParseInLocation("2006-01-02", p.EstablishDate, time.Local); err == nil { + supplier.EstablishDate = &t + } + } else { + supplier.EstablishDate = nil + } + + _, err = models.Orm.Update(&supplier) + if err != nil { + c.supplierJsonErr(500, 500, "更新失败: "+err.Error()) + return + } + + c.supplierOk(nil) +} + +// Delete DELETE /backend/erp/supplier/:id +func (c *BackendErpSupplierController) Delete() { + claims, err := c.supplierClaims() + if err != nil { + c.supplierJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.supplierJsonErr(400, 400, "无效的ID") + return + } + + now := time.Now() + _, err = models.Orm.QueryTable(new(models.TenantCrmSupplier)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Update(map[string]interface{}{ + "delete_time": now, + "update_time": now, + }) + if err != nil { + c.supplierJsonErr(500, 500, "删除失败: "+err.Error()) + return + } + + c.supplierOk(nil) +} + +// ToggleStatus POST /backend/erp/supplier/:id/status +func (c *BackendErpSupplierController) ToggleStatus() { + claims, err := c.supplierClaims() + if err != nil { + c.supplierJsonErr(401, 401, err.Error()) + return + } + + idStr := c.Ctx.Input.Param(":id") + id, _ := strconv.ParseUint(idStr, 10, 64) + if id == 0 { + c.supplierJsonErr(400, 400, "无效的ID") + return + } + + raw, _ := io.ReadAll(c.Ctx.Request.Body) + var p struct { + Status string `json:"status"` + } + _ = json.Unmarshal(raw, &p) + + _, err = models.Orm.QueryTable(new(models.TenantCrmSupplier)). + Filter("id", id). + Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). + Update(map[string]interface{}{ + "status": p.Status, + "update_time": time.Now(), + }) + if err != nil { + c.supplierJsonErr(500, 500, "操作失败: "+err.Error()) + return + } + + c.supplierOk(nil) +} diff --git a/go/controllers/backend_notify_config.go b/go/controllers/backend_notify_config.go new file mode 100644 index 0000000..1071561 --- /dev/null +++ b/go/controllers/backend_notify_config.go @@ -0,0 +1,136 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "io" + "strings" + "time" + + "server/models" + "server/pkg/jwtutil" + + beego "github.com/beego/beego/v2/server/web" +) + +// BackendNotifyConfigController 租户用户通知配置控制器 +type BackendNotifyConfigController struct { + beego.Controller +} + +func (c *BackendNotifyConfigController) ncClaims() (*jwtutil.Claims, error) { + auth := c.Ctx.Request.Header.Get("Authorization") + if auth == "" { + return nil, fmt.Errorf("未登录") + } + parts := strings.SplitN(auth, " ", 2) + if len(parts) != 2 || parts[0] != "Bearer" { + return nil, fmt.Errorf("认证信息格式错误") + } + claims, err := jwtutil.ParseToken(parts[1]) + if err != nil { + return nil, fmt.Errorf("无效的token") + } + if claims.UserType != "backend" { + return nil, fmt.Errorf("无权访问") + } + return claims, nil +} + +func (c *BackendNotifyConfigController) ncJsonErr(httpStatus, bizCode int, msg string) { + c.Ctx.Output.SetStatus(httpStatus) + c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} + _ = c.ServeJSON() +} + +func (c *BackendNotifyConfigController) ncOk(data interface{}) { + c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data} + _ = c.ServeJSON() +} + +type notifyConfigPayload struct { + Email string `json:"email"` + Phone string `json:"phone"` + BarkDeviceKey string `json:"bark_device_key"` + BarkServerURL string `json:"bark_server_url"` +} + +// GetConfig GET /backend/notify-config +// 获取当前登录用户的通知配置 +func (c *BackendNotifyConfigController) GetConfig() { + claims, err := c.ncClaims() + if err != nil { + c.ncJsonErr(401, 401, err.Error()) + return + } + + cfg := models.GetBackendUserNotifyConfig(uint64(claims.TenantId), uint64(claims.UserID)) + if cfg == nil { + // 未配置时返回空结构,前端可以直接展示空表单 + cfg = &models.BackendUserNotifyConfig{} + } + c.ncOk(cfg) +} + +// UpdateConfig PUT /backend/notify-config +// 更新当前登录用户的通知配置(不存在则创建) +func (c *BackendNotifyConfigController) UpdateConfig() { + claims, err := c.ncClaims() + if err != nil { + c.ncJsonErr(401, 401, err.Error()) + return + } + + raw, err := io.ReadAll(c.Ctx.Request.Body) + if err != nil { + c.ncJsonErr(400, 400, "参数错误") + return + } + var p notifyConfigPayload + if err := json.Unmarshal(raw, &p); err != nil { + c.ncJsonErr(400, 400, "参数错误") + return + } + + tid := uint64(claims.TenantId) + uid := uint64(claims.UserID) + now := time.Now() + + cfg := models.GetBackendUserNotifyConfig(tid, uid) + if cfg == nil { + cfg = &models.BackendUserNotifyConfig{ + Tid: tid, + UserID: uid, + CreateTime: now, + } + } + + cfg.Email = ncStrPtr(p.Email) + cfg.Phone = ncStrPtr(p.Phone) + cfg.BarkDeviceKey = ncStrPtr(p.BarkDeviceKey) + cfg.BarkServerURL = ncStrPtr(p.BarkServerURL) + cfg.UpdateTime = now + + if cfg.ID == 0 { + if _, err := models.Orm.Insert(cfg); err != nil { + c.ncJsonErr(500, 500, "保存失败: "+err.Error()) + return + } + } else { + if _, err := models.Orm.Update(cfg, "Email", "Phone", "BarkDeviceKey", "BarkServerURL", "UpdateTime"); err != nil { + c.ncJsonErr(500, 500, "更新失败: "+err.Error()) + return + } + } + + c.ncOk(nil) +} + +// ncStrPtr 字符串转指针,空字符串返回 nil +func ncStrPtr(s string) *string { + s = strings.TrimSpace(s) + if s == "" { + return nil + } + return &s +} diff --git a/go/models/backend_user_notify_config.go b/go/models/backend_user_notify_config.go new file mode 100644 index 0000000..bf9c041 --- /dev/null +++ b/go/models/backend_user_notify_config.go @@ -0,0 +1,32 @@ +package models + +import "time" + +// BackendUserNotifyConfig 租户用户通知配置表: yz_backend_user_notify_config +// 每个用户一条记录,保存自己的接收渠道配置(邮箱/手机号/Bark) +type BackendUserNotifyConfig struct { + ID uint64 `orm:"column(id);pk;auto" json:"id"` + Tid uint64 `orm:"column(tid)" json:"tid"` + UserID uint64 `orm:"column(user_id)" json:"user_id"` + Email *string `orm:"column(email);size(128);null" json:"email"` + Phone *string `orm:"column(phone);size(20);null" json:"phone"` + BarkDeviceKey *string `orm:"column(bark_device_key);size(128);null" json:"bark_device_key"` + BarkServerURL *string `orm:"column(bark_server_url);size(255);null" json:"bark_server_url"` // 为空用默认 https://api.day.app + CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"` + UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"` +} + +func (m *BackendUserNotifyConfig) TableName() string { + return "yz_backend_user_notify_config" +} + +// GetBackendUserNotifyConfig 按 tid+user_id 查询配置,不存在返回 nil +func GetBackendUserNotifyConfig(tid, userID uint64) *BackendUserNotifyConfig { + var cfg BackendUserNotifyConfig + err := Orm.QueryTable(new(BackendUserNotifyConfig)). + Filter("tid", tid).Filter("user_id", userID).One(&cfg) + if err != nil { + return nil + } + return &cfg +} diff --git a/go/models/contact.go b/go/models/contact.go index 3dfdd33..5eb9b10 100644 --- a/go/models/contact.go +++ b/go/models/contact.go @@ -32,5 +32,5 @@ type BackendErpContact struct { } func (m *BackendErpContact) TableName() string { - return "yz_backend_contact" + return "yz_backend_erp_contact" } diff --git a/go/models/erp_account_set.go b/go/models/erp_account_set.go new file mode 100644 index 0000000..050bbaa --- /dev/null +++ b/go/models/erp_account_set.go @@ -0,0 +1,27 @@ +package models + +import "time" + +// ErpAccountSet ERP账套管理表: yz_erp_account_set +type ErpAccountSet struct { + ID uint64 `orm:"column(id);pk;auto" json:"id"` + TenantID string `orm:"column(tenant_id);size(64)" json:"tenant_id"` + SetCode string `orm:"column(set_code);size(50)" json:"set_code"` + SetName string `orm:"column(set_name);size(100)" json:"set_name"` + CompanyName string `orm:"column(company_name);size(100)" json:"company_name"` + CompanyTaxNo string `orm:"column(company_tax_no);size(50)" json:"company_tax_no"` + StartYear int `orm:"column(start_year);default(2024)" json:"start_year"` + StartMonth int `orm:"column(start_month);default(1)" json:"start_month"` + AccountingStandard string `orm:"column(accounting_standard);size(20);default(1)" json:"accounting_standard"` + Currency string `orm:"column(currency);size(10);default(CNY)" json:"currency"` + TaxRate float64 `orm:"column(tax_rate);digits(5);decimals(2);default(13.00)" json:"tax_rate"` + Status string `orm:"column(status);size(20);default(1)" json:"status"` + Remark string `orm:"column(remark);type(text);null" json:"remark"` + CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"` + UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"` + DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"` +} + +func (m *ErpAccountSet) TableName() string { + return "yz_backend_erp_account_set" +} diff --git a/go/models/erp_company_contact.go b/go/models/erp_company_contact.go new file mode 100644 index 0000000..709360a --- /dev/null +++ b/go/models/erp_company_contact.go @@ -0,0 +1,32 @@ +package models + +import "time" + +// ErpCompanyContact 客户/供应商联系人表 +type ErpCompanyContact struct { + ID uint64 `orm:"column(id);pk;auto" json:"id"` + TenantID string `orm:"column(tenant_id);size(64)" json:"tenant_id"` + CompanyType string `orm:"column(company_type);size(20)" json:"company_type"` // customer / supplier + CompanyID uint64 `orm:"column(company_id)" json:"company_id"` + Name string `orm:"column(name);size(50)" json:"name"` + Gender int8 `orm:"column(gender);default(0)" json:"gender"` // 0-未知 1-男 2-女 + Phone string `orm:"column(phone);size(20)" json:"phone"` + Mobiles string `orm:"column(mobiles);type(text);null" json:"mobiles"` // JSON数组,最多5个 + Wechat string `orm:"column(wechat);size(50)" json:"wechat"` + QQ string `orm:"column(qq);size(50)" json:"qq"` + Dingtalk string `orm:"column(dingtalk);size(50)" json:"dingtalk"` + Email string `orm:"column(email);size(100)" json:"email"` + Department string `orm:"column(department);size(50)" json:"department"` + Position string `orm:"column(position);size(50)" json:"position"` + HomeAddress string `orm:"column(home_address);size(255)" json:"home_address"` + IsPrimary int8 `orm:"column(is_primary);default(0)" json:"is_primary"` // 0-否 1-是 + Status int8 `orm:"column(status);default(1)" json:"status"` // 0-离职 1-在职 + Remark string `orm:"column(remark);type(text);null" json:"remark"` + CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"` + UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"` + DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"` +} + +func (m *ErpCompanyContact) TableName() string { + return "yz_backend_erp_company_contact" +} diff --git a/go/models/erp_normal_setting.go b/go/models/erp_normal_setting.go new file mode 100644 index 0000000..a3e469d --- /dev/null +++ b/go/models/erp_normal_setting.go @@ -0,0 +1,48 @@ +package models + +import "time" + +// ErpNormalSetting ERP基础设置表(键值对方式): yz_erp_normalsettings +// 每个租户可配置多条基础参数,按 config_group 分组展示 +type ErpNormalSetting struct { + ID uint64 `orm:"column(id);pk;auto" json:"id"` + TenantID string `orm:"column(tenant_id);size(64)" json:"tenant_id"` + ConfigKey string `orm:"column(config_key);size(100)" json:"config_key"` + ConfigValue string `orm:"column(config_value);type(text);null" json:"config_value"` + ConfigName string `orm:"column(config_name);size(100)" json:"config_name"` + ConfigGroup string `orm:"column(config_group);size(50)" json:"config_group"` + ConfigType string `orm:"column(config_type);size(20);default(input)" json:"config_type"` // input/select/number/textarea/switch + ConfigOptions string `orm:"column(config_options);type(text);null" json:"config_options"` // select 类型的选项,JSON格式 + SortOrder int `orm:"column(sort_order);default(0)" json:"sort_order"` + Status int8 `orm:"column(status);default(1)" json:"status"` + CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"` + UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"` +} + +func (m *ErpNormalSetting) TableName() string { + return "yz_backend_erp_normalsettings" +} + +// GetErpNormalSettingsByTenant 获取租户所有启用的基础设置 +func GetErpNormalSettingsByTenant(tenantID string) ([]ErpNormalSetting, error) { + var list []ErpNormalSetting + _, err := Orm.QueryTable(new(ErpNormalSetting)). + Filter("tenant_id", tenantID). + Filter("status", 1). + OrderBy("config_group", "sort_order", "id"). + All(&list) + return list, err +} + +// GetErpNormalSettingByKey 按租户+key获取配置 +func GetErpNormalSettingByKey(tenantID, key string) *ErpNormalSetting { + var setting ErpNormalSetting + err := Orm.QueryTable(new(ErpNormalSetting)). + Filter("tenant_id", tenantID). + Filter("config_key", key). + One(&setting) + if err != nil { + return nil + } + return &setting +} diff --git a/go/models/init.go b/go/models/init.go index 9a51ccc..5b3273d 100644 --- a/go/models/init.go +++ b/go/models/init.go @@ -71,6 +71,12 @@ func Init(_ string) { new(PlatformAgentApi), new(PlatformPasswordStore), new(BackendPasswordStore), + new(BackendUserNotifyConfig), + new(TenantCrmCustomer), + new(TenantCrmSupplier), + new(ErpAccountSet), + new(ErpNormalSetting), + new(ErpCompanyContact), new(CmsArticleCategory), new(CmsArticle), diff --git a/go/models/tenant_crm_customer.go b/go/models/tenant_crm_customer.go new file mode 100644 index 0000000..2526d77 --- /dev/null +++ b/go/models/tenant_crm_customer.go @@ -0,0 +1,42 @@ +package models + +import "time" + +// TenantCrmCustomer 客户管理表: yz_tenant_crm_customer +type TenantCrmCustomer struct { + ID uint64 `orm:"column(id);pk;auto" json:"id"` + TenantID string `orm:"column(tenant_id);size(64)" json:"tenant_id"` + CustomerName string `orm:"column(customer_name);size(100)" json:"customer_name"` + CustomerType string `orm:"column(customer_type);size(20)" json:"customer_type"` + ContactPerson string `orm:"column(contact_person);size(50)" json:"contact_person"` + ContactPhone string `orm:"column(contact_phone);size(20)" json:"contact_phone"` + ContactEmail string `orm:"column(contact_email);size(100)" json:"contact_email"` + CustomerLevel string `orm:"column(customer_level);size(20);default(3)" json:"customer_level"` + Industry string `orm:"column(industry);size(50)" json:"industry"` + RegisteredCapital string `orm:"column(registered_capital);size(50)" json:"registered_capital"` + PaidCapital string `orm:"column(paid_capital);size(50)" json:"paid_capital"` + EstablishDate *time.Time `orm:"column(establish_date);type(date);null" json:"establish_date"` + AdministrativeDivision string `orm:"column(administrative_division);size(100)" json:"administrative_division"` + EnterpriseType string `orm:"column(enterprise_type);size(50)" json:"enterprise_type"` + TaxpayerQualification string `orm:"column(taxpayer_qualification);size(50)" json:"taxpayer_qualification"` + BusinessScope string `orm:"column(business_scope);type(text);null" json:"business_scope"` + Address string `orm:"column(address);size(255)" json:"address"` + RegisterTime *time.Time `orm:"column(register_time);type(date);null" json:"register_time"` + ExpireTime *time.Time `orm:"column(expire_time);type(date);null" json:"expire_time"` + Status string `orm:"column(status);size(20);default(1)" json:"status"` + IsDraft int8 `orm:"column(is_draft);default(0)" json:"is_draft"` // 0-正式 1-草稿 + Remark string `orm:"column(remark);type(text);null" json:"remark"` + InvoiceTitle string `orm:"column(invoice_title);size(100)" json:"invoice_title"` + TaxNumber string `orm:"column(tax_number);size(50)" json:"tax_number"` + BankName string `orm:"column(bank_name);size(100)" json:"bank_name"` + BankAccount string `orm:"column(bank_account);size(50)" json:"bank_account"` + RegisteredAddress string `orm:"column(registered_address);size(255)" json:"registered_address"` + RegisteredPhone string `orm:"column(registered_phone);size(50)" json:"registered_phone"` + CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"` + UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"` + DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"` +} + +func (m *TenantCrmCustomer) TableName() string { + return "yz_backend_erp_customer" +} diff --git a/go/models/tenant_crm_supplier.go b/go/models/tenant_crm_supplier.go new file mode 100644 index 0000000..c3cd473 --- /dev/null +++ b/go/models/tenant_crm_supplier.go @@ -0,0 +1,42 @@ +package models + +import "time" + +// TenantCrmSupplier 供应商管理表: yz_tenant_crm_supplier +type TenantCrmSupplier struct { + ID uint64 `orm:"column(id);pk;auto" json:"id"` + TenantID string `orm:"column(tenant_id);size(64)" json:"tenant_id"` + SupplierName string `orm:"column(supplier_name);size(100)" json:"supplier_name"` + SupplierType string `orm:"column(supplier_type);size(20)" json:"supplier_type"` + ContactPerson string `orm:"column(contact_person);size(50)" json:"contact_person"` + ContactPhone string `orm:"column(contact_phone);size(20)" json:"contact_phone"` + ContactEmail string `orm:"column(contact_email);size(100)" json:"contact_email"` + SupplierLevel string `orm:"column(supplier_level);size(20);default(3)" json:"supplier_level"` + Industry string `orm:"column(industry);size(50)" json:"industry"` + RegisteredCapital string `orm:"column(registered_capital);size(50)" json:"registered_capital"` + PaidCapital string `orm:"column(paid_capital);size(50)" json:"paid_capital"` + EstablishDate *time.Time `orm:"column(establish_date);type(date);null" json:"establish_date"` + AdministrativeDivision string `orm:"column(administrative_division);size(100)" json:"administrative_division"` + EnterpriseType string `orm:"column(enterprise_type);size(50)" json:"enterprise_type"` + TaxpayerQualification string `orm:"column(taxpayer_qualification);size(50)" json:"taxpayer_qualification"` + BusinessScope string `orm:"column(business_scope);type(text);null" json:"business_scope"` + Address string `orm:"column(address);size(255)" json:"address"` + RegisterTime *time.Time `orm:"column(register_time);type(date);null" json:"register_time"` + ExpireTime *time.Time `orm:"column(expire_time);type(date);null" json:"expire_time"` + Status string `orm:"column(status);size(20);default(1)" json:"status"` + IsDraft int8 `orm:"column(is_draft);default(0)" json:"is_draft"` // 0-正式 1-草稿 + Remark string `orm:"column(remark);type(text);null" json:"remark"` + InvoiceTitle string `orm:"column(invoice_title);size(100)" json:"invoice_title"` + TaxNumber string `orm:"column(tax_number);size(50)" json:"tax_number"` + BankName string `orm:"column(bank_name);size(100)" json:"bank_name"` + BankAccount string `orm:"column(bank_account);size(50)" json:"bank_account"` + RegisteredAddress string `orm:"column(registered_address);size(255)" json:"registered_address"` + RegisteredPhone string `orm:"column(registered_phone);size(50)" json:"registered_phone"` + CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"` + UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"` + DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"` +} + +func (m *TenantCrmSupplier) TableName() string { + return "yz_backend_erp_supplier" +} diff --git a/go/routers/backend/backend.go b/go/routers/backend/backend.go index d6c3f5f..b733135 100644 --- a/go/routers/backend/backend.go +++ b/go/routers/backend/backend.go @@ -1,4 +1,4 @@ -package backend +package backend import ( "server/controllers" @@ -244,6 +244,9 @@ func RegisterAuthRoutes() { beego.Router("/backend/reminder/batchDelete", &controllers.BackendReminderController{}, "post:BatchDeleteReminder") beego.Router("/backend/reminder/finish/:id", &controllers.BackendReminderController{}, "post:FinishReminder") + // 用户通知配置(每个用户自己的邮箱/手机号/Bark) + beego.Router("/backend/notify-config", &controllers.BackendNotifyConfigController{}, "get:GetConfig;put:UpdateConfig") + // OA 薪酬管理(薪酬方案 / 月度薪资单) beego.Router("/backend/oa/compensation/employees", &controllers.BackendOaCompensationController{}, "get:Employees") beego.Router("/backend/oa/compensation/dashboard", &controllers.BackendOaCompensationController{}, "get:Dashboard") @@ -308,4 +311,31 @@ func registerOrganizationRoutes(module string) { beego.Router(prefix+"/exportOrganization", &controllers.BackendOrganizationController{}, "get:ExportOrganization") beego.Router(prefix+"/importOrganization", &controllers.BackendOrganizationController{}, "post:ImportOrganization") beego.Router(prefix+"/organizationImportTemplate", &controllers.BackendOrganizationController{}, "get:GetImportTemplate") + + // ERP客户管理 + beego.Router("/backend/erp/customer/list", &controllers.BackendErpCustomerController{}, "get:List") + beego.Router("/backend/erp/customer", &controllers.BackendErpCustomerController{}, "post:Create") + beego.Router("/backend/erp/customer/:id", &controllers.BackendErpCustomerController{}, "get:Detail;put:Update;delete:Delete") + beego.Router("/backend/erp/customer/:id/status", &controllers.BackendErpCustomerController{}, "post:ToggleStatus") + + // ERP供应商管理 + beego.Router("/backend/erp/supplier/list", &controllers.BackendErpSupplierController{}, "get:List") + beego.Router("/backend/erp/supplier", &controllers.BackendErpSupplierController{}, "post:Create") + beego.Router("/backend/erp/supplier/:id", &controllers.BackendErpSupplierController{}, "get:Detail;put:Update;delete:Delete") + beego.Router("/backend/erp/supplier/:id/status", &controllers.BackendErpSupplierController{}, "post:ToggleStatus") + + // ERP账套管理 + beego.Router("/backend/erp/account-set/list", &controllers.BackendErpAccountSetController{}, "get:List") + beego.Router("/backend/erp/account-set", &controllers.BackendErpAccountSetController{}, "post:Create") + beego.Router("/backend/erp/account-set/:id", &controllers.BackendErpAccountSetController{}, "get:Detail;put:Update;delete:Delete") + beego.Router("/backend/erp/account-set/:id/status", &controllers.BackendErpAccountSetController{}, "post:ToggleStatus") + + // ERP基础设置 + beego.Router("/backend/erp/normal-settings", &controllers.BackendErpNormalSettingController{}, "get:List;put:Save") + beego.Router("/backend/erp/normal-settings/reset", &controllers.BackendErpNormalSettingController{}, "post:Reset") + + // ERP客户/供应商联系人 + beego.Router("/backend/erp/company-contact/list", &controllers.BackendErpCompanyContactController{}, "get:List") + beego.Router("/backend/erp/company-contact", &controllers.BackendErpCompanyContactController{}, "post:Create") + beego.Router("/backend/erp/company-contact/:id", &controllers.BackendErpCompanyContactController{}, "put:Update;delete:Delete") } diff --git a/go/services/reminder_scheduler.go b/go/services/reminder_scheduler.go index a724264..da18279 100644 --- a/go/services/reminder_scheduler.go +++ b/go/services/reminder_scheduler.go @@ -28,6 +28,90 @@ type ReminderSender interface { Send(ctx context.Context, reminder ReminderData, title, content string) (success bool, err error) } +// resolveReceiverTarget 统一解析接收目标 +// 优先级(从高到低): +// 1. 单条提醒指定的 ReceiverTarget(创建提醒时手动填写) +// 2. backend 用户通知配置表(yz_backend_user_notify_config) +// 3. 用户基础信息表(backend 查 SystemTenantUser,platform 查 AdminUser) +// +// channel: SMS / EMAIL / BARK +// data.Tid > 0 表示 backend 租户用户,Tid == 0 表示 platform 管理员 +func resolveReceiverTarget(channel string, data ReminderData) string { + // 1. 单条提醒指定的目标最高优先 + if data.ReceiverTarget != nil && *data.ReceiverTarget != "" { + return *data.ReceiverTarget + } + + // 2. backend 用户查用户通知配置表 + if data.Tid > 0 { + cfg := models.GetBackendUserNotifyConfig(uint64(data.Tid), data.ReceiverUserID) + if cfg != nil { + switch channel { + case "SMS": + if cfg.Phone != nil && *cfg.Phone != "" { + return *cfg.Phone + } + case "EMAIL": + if cfg.Email != nil && *cfg.Email != "" { + return *cfg.Email + } + case "BARK": + if cfg.BarkDeviceKey != nil && *cfg.BarkDeviceKey != "" { + return *cfg.BarkDeviceKey + } + } + } + } + + // 3. 最后 fallback 到用户基础信息表 + if data.Tid > 0 { + // backend 租户用户 + var user models.SystemTenantUser + if err := models.Orm.QueryTable(new(models.SystemTenantUser)). + Filter("id", data.ReceiverUserID).One(&user); err == nil { + switch channel { + case "SMS": + if user.Phone != nil && *user.Phone != "" { + return *user.Phone + } + case "EMAIL": + if user.Email != nil && *user.Email != "" { + return *user.Email + } + } + } + } else { + // platform 管理员 + var user models.AdminUser + if err := models.Orm.QueryTable(new(models.AdminUser)). + Filter("id", data.ReceiverUserID).One(&user); err == nil { + switch channel { + case "SMS": + if user.Phone != nil && *user.Phone != "" { + return *user.Phone + } + case "EMAIL": + if user.Email != nil && *user.Email != "" { + return *user.Email + } + } + } + } + return "" +} + +// resolveBarkServerURL 解析 Bark 服务端地址 +// backend 用户优先用自己配置的 server URL,否则用平台默认配置 +func resolveBarkServerURL(data ReminderData) string { + if data.Tid > 0 { + cfg := models.GetBackendUserNotifyConfig(uint64(data.Tid), data.ReceiverUserID) + if cfg != nil && cfg.BarkServerURL != nil && *cfg.BarkServerURL != "" { + return *cfg.BarkServerURL + } + } + return models.GetPlatformSettingValue("bark_server_url", "https://api.day.app") +} + // SMSSender 短信发送实现 type SMSSender struct{} @@ -36,17 +120,10 @@ func (s *SMSSender) Send(ctx context.Context, reminder ReminderData, title, cont if err != nil { return false, err } - phone := "" - if reminder.ReceiverTarget != nil && *reminder.ReceiverTarget != "" { - phone = *reminder.ReceiverTarget - } else { - var user models.AdminUser - if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", reminder.ReceiverUserID).One(&user); err == nil && user.Phone != nil { - phone = *user.Phone - } - } + + phone := resolveReceiverTarget("SMS", reminder) if phone == "" { - return false, fmt.Errorf("未配置手机号") + return false, fmt.Errorf("未配置手机号(请在通知设置中配置,或在创建提醒时指定)") } enqueueURL := strings.TrimRight(backendURL, "/") + "/api/v1/business/outbound-tasks" @@ -91,17 +168,9 @@ func (s *EmailSender) Send(ctx context.Context, reminder ReminderData, title, co return false, fmt.Errorf("未配置系统邮箱") } - toEmail := "" - if reminder.ReceiverTarget != nil && *reminder.ReceiverTarget != "" { - toEmail = *reminder.ReceiverTarget - } else { - var user models.AdminUser - if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", reminder.ReceiverUserID).One(&user); err == nil && user.Email != nil { - toEmail = *user.Email - } - } + toEmail := resolveReceiverTarget("EMAIL", reminder) if toEmail == "" { - return false, fmt.Errorf("未配置收件邮箱") + return false, fmt.Errorf("未配置收件邮箱(请在通知设置中配置,或在创建提醒时指定)") } sysDomain := models.GetPlatformSettingValue("system_domain", "https://api.yunzer.cn") @@ -156,17 +225,12 @@ func (s *EmailSender) Send(ctx context.Context, reminder ReminderData, title, co type BarkSender struct{} func (s *BarkSender) Send(ctx context.Context, reminder ReminderData, title, content string) (bool, error) { - deviceKey := "" - if reminder.ReceiverTarget != nil && *reminder.ReceiverTarget != "" { - deviceKey = *reminder.ReceiverTarget - } else { - deviceKey = models.GetPlatformSettingValue("bark_device_key", "") - } + deviceKey := resolveReceiverTarget("BARK", reminder) if deviceKey == "" { - return false, fmt.Errorf("Bark 设备 Key 未配置") + return false, fmt.Errorf("Bark 设备 Key 未配置(请在通知设置中配置,或在创建提醒时指定)") } - serverURL := models.GetPlatformSettingValue("bark_server_url", "https://api.day.app") + serverURL := resolveBarkServerURL(reminder) sysDomain := models.GetPlatformSettingValue("system_domain", "https://api.yunzer.cn") ackToken := "" if reminder.AckToken != nil {