diff --git a/api/externals/controller/division_controller.go b/api/externals/controller/division_controller.go new file mode 100644 index 00000000..973c1f6c --- /dev/null +++ b/api/externals/controller/division_controller.go @@ -0,0 +1,81 @@ +package controller + +import ( + "net/http" + + "github.com/NUTFes/FinanSu/api/generated" + "github.com/NUTFes/FinanSu/api/internals/usecase" + "github.com/labstack/echo/v4" +) + +type divisionController struct { + u usecase.DivisionUseCase +} + +type DivisionController interface { + IndexDivisions(echo.Context) error + CreateDivision(echo.Context) error + UpdateDivision(echo.Context) error + DestroyDivision(echo.Context) error +} + +func NewDivisionController(u usecase.DivisionUseCase) DivisionController { + return &divisionController{u} +} + +func (d *divisionController) IndexDivisions(c echo.Context) error { + ctx := c.Request().Context() + year := c.QueryParam("year") + financialRecordId := c.QueryParam("financial_record_id") + + divisionDetails, err := d.u.GetDivisions(ctx, year, financialRecordId) + if err != nil { + return err + } + return c.JSON(http.StatusOK, divisionDetails) +} + +func (d *divisionController) CreateDivision(c echo.Context) error { + ctx := c.Request().Context() + division := new(Division) + + if err := c.Bind(division); err != nil { + return c.String(http.StatusBadRequest, "Bad Request") + } + latestDivision, err := d.u.CreateDivision(ctx, *division) + if err != nil { + return err + } + return c.JSON(http.StatusOK, latestDivision) +} + +func (d *divisionController) UpdateDivision(c echo.Context) error { + ctx := c.Request().Context() + id := c.Param("id") + division := new(Division) + + if err := c.Bind(division); err != nil { + return c.String(http.StatusBadRequest, "Bad Request") + } + updatedDivision, err := d.u.UpdateDivision(ctx, id, *division) + if err != nil { + return err + } + return c.JSON(http.StatusOK, updatedDivision) +} + +func (d *divisionController) DestroyDivision(c echo.Context) error { + ctx := c.Request().Context() + id := c.Param("id") + + err := d.u.DestroyDivision(ctx, id) + if err != nil { + return err + } + return c.String(http.StatusOK, "Destroy Division") +} + +type ( + Division = generated.Division + DivisionDetails = generated.DivisionDetails +) diff --git a/api/externals/repository/division_repository.go b/api/externals/repository/division_repository.go new file mode 100644 index 00000000..0ad97afc --- /dev/null +++ b/api/externals/repository/division_repository.go @@ -0,0 +1,140 @@ +package repository + +import ( + "context" + "database/sql" + + "github.com/NUTFes/FinanSu/api/drivers/db" + "github.com/NUTFes/FinanSu/api/externals/repository/abstract" + "github.com/NUTFes/FinanSu/api/generated" + goqu "github.com/doug-martin/goqu/v9" +) + +type divisionRepository struct { + client db.Client + crud abstract.Crud +} + +type DivisionRepository interface { + AllByPeriodAndFinancialRecord(context.Context, string, string) (*sql.Rows, error) + GetById(context.Context, string) (*sql.Row, error) + Create(context.Context, Division) error + Update(context.Context, string, Division) error + Delete(context.Context, string) error + FindLatestRecord(context.Context) (*sql.Row, error) +} + +func NewDivisionRepository(c db.Client, ac abstract.Crud) DivisionRepository { + return &divisionRepository{c, ac} +} + +// 年度別と財務記録で取得 +func (dr *divisionRepository) AllByPeriodAndFinancialRecord( + c context.Context, + year string, + financialRecordId string, +) (*sql.Rows, error) { + + ds := selectDivisionQuery + + if year != "" { + ds = ds.Where(goqu.Ex{"years.year": year}) + } + if financialRecordId != "" { + ds = ds.Where(goqu.Ex{"financial_records.id": financialRecordId}) + } + + // クエリを構築し、SQLを生成 + query, _, err := ds.ToSQL() + if err != nil { + return nil, err + } + return dr.crud.Read(c, query) +} + +// IDで取得 +func (dr *divisionRepository) GetById( + c context.Context, + id string, +) (*sql.Row, error) { + ds, _, err := selectDivisionQuery. + Where(goqu.Ex{"divisions.id": id}). + ToSQL() + if err != nil { + return nil, err + } + return dr.crud.ReadByID(c, ds) +} + +// 部門作成 +func (dr *divisionRepository) Create( + c context.Context, + division Division, +) error { + ds := dialect.Insert("divisions"). + Rows(goqu.Record{"name": division.Name, "financial_record_id": division.FinancialRecordID}) + query, _, err := ds.ToSQL() + if err != nil { + return err + } + return dr.crud.UpdateDB(c, query) +} + +// 部門更新 +func (dr *divisionRepository) Update( + c context.Context, + id string, + division Division, +) error { + ds := dialect.Update("divisions"). + Set(goqu.Record{"name": division.Name, "financial_record_id": division.FinancialRecordID}). + Where(goqu.Ex{"id": id}) + query, _, err := ds.ToSQL() + if err != nil { + return err + } + return dr.crud.UpdateDB(c, query) +} + +// 部門削除 +func (dr *divisionRepository) Delete( + c context.Context, + id string, +) error { + ds := dialect.Delete("divisions").Where(goqu.Ex{"id": id}) + query, _, err := ds.ToSQL() + if err != nil { + return err + } + return dr.crud.UpdateDB(c, query) +} + +// 最新の部門を取得する +func (dr *divisionRepository) FindLatestRecord(c context.Context) (*sql.Row, error) { + ds := selectDivisionQuery + query, _, err := ds.Limit(1).ToSQL() + + if err != nil { + return nil, err + } + return dr.crud.ReadByID(c, query) +} + +type Division = generated.Division + +// NOTE: getの共通部分抜き出し +var selectDivisionQuery = dialect.From("divisions"). + Select( + "divisions.id", + "divisions.name", + "financial_records.name", + goqu.COALESCE(goqu.SUM("item_budgets.amount"), 0).As("budget"), + goqu.COALESCE(goqu.SUM("buy_reports.amount"), 0).As("expense"), + goqu.L("COALESCE(SUM(item_budgets.amount), 0) - COALESCE(SUM(buy_reports.amount), 0)").As("balance")). + InnerJoin(goqu.I("financial_records"), goqu.On(goqu.I("financial_records.id").Eq(goqu.I("divisions.financial_record_id")))). + InnerJoin(goqu.I("years"), goqu.On(goqu.I("financial_records.year_id").Eq(goqu.I("years.id")))). + LeftJoin(goqu.I("festival_items"), goqu.On(goqu.I("divisions.id").Eq(goqu.I("festival_items.division_id")))). + LeftJoin(goqu.I("item_budgets"), goqu.On(goqu.I("festival_items.id").Eq(goqu.I("item_budgets.festival_item_id")))). + LeftJoin(goqu.I("buy_reports"), goqu.On(goqu.I("festival_items.id").Eq(goqu.I("buy_reports.festival_item_id")))). + GroupBy(goqu.I("divisions.id")). + Order(goqu.I("divisions.id").Desc()) diff --git a/api/generated/openapi_gen.go b/api/generated/openapi_gen.go index 9ea10317..79968fba 100644 --- a/api/generated/openapi_gen.go +++ b/api/generated/openapi_gen.go @@ -345,6 +345,15 @@ type PutDepartmentsIdParams struct { Name *string `form:"name,omitempty" json:"name,omitempty"` } +// GetDivisionsParams defines parameters for GetDivisions. +type GetDivisionsParams struct { + // Year year + Year *int `form:"year,omitempty" json:"year,omitempty"` + + // FinancialRecordId financial_record_id + FinancialRecordId *int `form:"financial_record_id,omitempty" json:"financial_record_id,omitempty"` +} + // PostExpensesParams defines parameters for PostExpenses. type PostExpensesParams struct { // Name name @@ -741,7 +750,7 @@ type ServerInterface interface { PutDepartmentsId(ctx echo.Context, id int, params PutDepartmentsIdParams) error // (GET /divisions) - GetDivisions(ctx echo.Context) error + GetDivisions(ctx echo.Context, params GetDivisionsParams) error // (POST /divisions) PostDivisions(ctx echo.Context) error @@ -1674,8 +1683,24 @@ func (w *ServerInterfaceWrapper) PutDepartmentsId(ctx echo.Context) error { func (w *ServerInterfaceWrapper) GetDivisions(ctx echo.Context) error { var err error + // Parameter object where we will unmarshal all parameters from the context + var params GetDivisionsParams + // ------------- Optional query parameter "year" ------------- + + err = runtime.BindQueryParameter("form", true, false, "year", ctx.QueryParams(), ¶ms.Year) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter year: %s", err)) + } + + // ------------- Optional query parameter "financial_record_id" ------------- + + err = runtime.BindQueryParameter("form", true, false, "financial_record_id", ctx.QueryParams(), ¶ms.FinancialRecordId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter financial_record_id: %s", err)) + } + // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetDivisions(ctx) + err = w.Handler.GetDivisions(ctx, params) return err } diff --git a/api/go.sum b/api/go.sum index 0894eda3..028832e8 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,3 +1,7 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= @@ -48,6 +52,7 @@ github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zG github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -72,6 +77,8 @@ github.com/slack-go/slack v0.13.0 h1:7my/pR2ubZJ9912p9FtvALYpbt0cQPAqkRy2jaSI1PQ github.com/slack-go/slack v0.13.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/api/internals/di/di.go b/api/internals/di/di.go index 32b9630f..45a1661c 100644 --- a/api/internals/di/di.go +++ b/api/internals/di/di.go @@ -37,6 +37,7 @@ func InitializeServer() db.Client { budgetRepository := repository.NewBudgetRepository(client, crud) bureauRepository := repository.NewBureauRepository(client, crud) departmentRepository := repository.NewDepartmentRepository(client, crud) + divisionRepository := repository.NewDivisionRepository(client, crud) expenseRepository := repository.NewExpenseRepository(client, crud) festivalItemRepository := repository.NewFestivalItemRepository(client, crud) financialRecordRepository := repository.NewFinancialRecordRepository(client, crud) @@ -68,6 +69,7 @@ func InitializeServer() db.Client { budgetUseCase := usecase.NewBudgetUseCase(budgetRepository) bureauUseCase := usecase.NewBureauUseCase(bureauRepository) departmentUseCase := usecase.NewDepartmentUseCase(departmentRepository) + divisionUseCase := usecase.NewDivisionUseCase(divisionRepository) expenseUseCase := usecase.NewExpenseUseCase(expenseRepository) festivalUseCase := usecase.NewFestivalItemUseCase(festivalItemRepository) financialRecordUseCase := usecase.NewFinancialRecordUseCase(financialRecordRepository) @@ -104,6 +106,7 @@ func InitializeServer() db.Client { budgetController := controller.NewBudgetController(budgetUseCase) bureauController := controller.NewBureauController(bureauUseCase) departmentController := controller.NewDepartmentController(departmentUseCase) + divisionController := controller.NewDivisionController(divisionUseCase) expenseController := controller.NewExpenseController(expenseUseCase) festivalItemController := controller.NewFestivalItemController(festivalUseCase) financialRecordController := controller.NewFinancialRecordController(financialRecordUseCase) @@ -134,6 +137,7 @@ func InitializeServer() db.Client { budgetController, bureauController, departmentController, + divisionController, expenseController, festivalItemController, financialRecordController, diff --git a/api/internals/usecase/division_usecase.go b/api/internals/usecase/division_usecase.go new file mode 100644 index 00000000..c01107d6 --- /dev/null +++ b/api/internals/usecase/division_usecase.go @@ -0,0 +1,143 @@ +package usecase + +import ( + "context" + + rep "github.com/NUTFes/FinanSu/api/externals/repository" + "github.com/NUTFes/FinanSu/api/generated" +) + +type divisionUseCase struct { + rep rep.DivisionRepository +} + +type DivisionUseCase interface { + GetDivisions(context.Context, string, string) (DivisionDetails, error) + CreateDivision(context.Context, Division) (DivisionWithBalance, error) + UpdateDivision(context.Context, string, Division) (DivisionWithBalance, error) + DestroyDivision(context.Context, string) error +} + +func NewDivisionUseCase(rep rep.DivisionRepository) DivisionUseCase { + return &divisionUseCase{rep} +} + +func (du divisionUseCase) GetDivisions(c context.Context, year string, financialRecordId string) (DivisionDetails, error) { + var details DivisionDetails + var divisions []DivisionWithBalance + + rows, err := du.rep.AllByPeriodAndFinancialRecord(c, year, financialRecordId) + if err != nil { + return DivisionDetails{}, err + } + defer rows.Close() + + for rows.Next() { + var division DivisionWithBalance + err := rows.Scan( + &division.Id, + &division.Name, + &division.FinancialRecord, + &division.Budget, + &division.Expense, + &division.Balance, + ) + if err != nil { + return DivisionDetails{}, err + } + divisions = append(divisions, division) + } + + details.Divisions = &divisions + + var total Total + budgetTotal := 0 + expenseTotal := 0 + balanceTotal := 0 + + for _, division := range divisions { + budgetTotal += *division.Budget + expenseTotal += *division.Expense + balanceTotal += *division.Balance + } + + total.Budget = &budgetTotal + total.Expense = &expenseTotal + total.Balance = &balanceTotal + details.Total = &total + + return details, nil +} + +func (du *divisionUseCase) CreateDivision( + c context.Context, + division Division, +) (DivisionWithBalance, error) { + latestDivisionWithBalance := DivisionWithBalance{} + + if err := du.rep.Create(c, division); err != nil { + return latestDivisionWithBalance, err + } + + row, err := du.rep.FindLatestRecord(c) + if err != nil { + return latestDivisionWithBalance, err + } + err = row.Scan( + &latestDivisionWithBalance.Id, + &latestDivisionWithBalance.Name, + &latestDivisionWithBalance.FinancialRecord, + &latestDivisionWithBalance.Budget, + &latestDivisionWithBalance.Expense, + &latestDivisionWithBalance.Balance, + ) + if err != nil { + return latestDivisionWithBalance, err + } + + return latestDivisionWithBalance, nil +} + +func (du *divisionUseCase) UpdateDivision( + c context.Context, + id string, + division Division, +) (DivisionWithBalance, error) { + updatedDivisionWithBalance := DivisionWithBalance{} + + if err := du.rep.Update(c, id, division); err != nil { + return updatedDivisionWithBalance, err + } + + row, err := du.rep.GetById(c, id) + if err != nil { + return updatedDivisionWithBalance, err + } + err = row.Scan( + &updatedDivisionWithBalance.Id, + &updatedDivisionWithBalance.Name, + &updatedDivisionWithBalance.FinancialRecord, + &updatedDivisionWithBalance.Budget, + &updatedDivisionWithBalance.Expense, + &updatedDivisionWithBalance.Balance, + ) + if err != nil { + return updatedDivisionWithBalance, err + } + + return updatedDivisionWithBalance, nil +} + +func (du *divisionUseCase) DestroyDivision(c context.Context, id string) error { + + if err := du.rep.Delete(c, id); err != nil { + return err + } + return nil +} + +type ( + Division = generated.Division + DivisionDetails = generated.DivisionDetails + DivisionWithBalance = generated.DivisionWithBalance +) diff --git a/api/router/router.go b/api/router/router.go index 4e4d031d..d8cf0cf6 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -15,6 +15,7 @@ type router struct { expenseController controller.ExpenseController festivalItemController controller.FestivalItemController financialRecordController controller.FinancialRecordController + divisionController controller.DivisionController fundInformationController controller.FundInformationController healthcheckController controller.HealthcheckController mailAuthController controller.MailAuthController @@ -43,6 +44,7 @@ func NewRouter( budgetController controller.BudgetController, bureauController controller.BureauController, departmentController controller.DepartmentController, + divisionController controller.DivisionController, expenseController controller.ExpenseController, festivalItemController controller.FestivalItemController, financialRecordController controller.FinancialRecordController, @@ -72,6 +74,7 @@ func NewRouter( expenseController, festivalItemController, financialRecordController, + divisionController, fundInformationController, healthController, mailAuthController, @@ -152,6 +155,12 @@ func (r router) ProvideRouter(e *echo.Echo) { e.PUT("/departments/:id", r.departmentController.UpdateDepartment) e.DELETE("/departments/:id", r.departmentController.DestroyDepartment) + // divisions + e.GET("/divisions", r.divisionController.IndexDivisions) + e.POST("/divisions", r.divisionController.CreateDivision) + e.PUT("/divisions/:id", r.divisionController.UpdateDivision) + e.DELETE("/divisions/:id", r.divisionController.DestroyDivision) + // expenseのRoute e.GET("/expenses", r.expenseController.IndexExpense) e.GET("/expenses/updateTP", r.expenseController.UpdateExpenseTP) diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index f65bb901..1bc12fb0 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -893,7 +893,19 @@ paths: get: tags: - division - description: divisionの一覧の取得 + description: division一覧の取得 + クエリでyearを指定することで年度ごとのdivisionを取得可能 + parameters: + - name: year + in: query + description: year + schema: + type: integer + - name: financial_record_id + in: query + description: financial_record_id + schema: + type: integer responses: "200": description: divisionの一覧を取得 diff --git a/view/next-project/src/generated/hooks.ts b/view/next-project/src/generated/hooks.ts index 6f50ce76..16cd73a2 100644 --- a/view/next-project/src/generated/hooks.ts +++ b/view/next-project/src/generated/hooks.ts @@ -77,6 +77,7 @@ import type { GetBuyReportsListParams, GetDepartments200, GetDepartmentsId200, + GetDivisionsParams, GetExpenses200, GetExpensesDetails200, GetExpensesDetailsYear200, @@ -2534,7 +2535,7 @@ export const useDeleteDepartmentsId = ( } /** - * divisionの一覧の取得 + * division一覧の取得 クエリでyearを指定することで年度ごとのdivisionを取得可能 */ export type getDivisionsResponse = { data: DivisionDetails; @@ -2542,15 +2543,22 @@ export type getDivisionsResponse = { headers: Headers; } -export const getGetDivisionsUrl = () => { +export const getGetDivisionsUrl = (params?: GetDivisionsParams,) => { + const normalizedParams = new URLSearchParams(); + Object.entries(params || {}).forEach(([key, value]) => { + + if (value !== undefined) { + normalizedParams.append(key, value === null ? 'null' : value.toString()) + } + }); - return `/divisions` + return normalizedParams.size ? `/divisions?${normalizedParams.toString()}` : `/divisions` } -export const getDivisions = async ( options?: RequestInit): Promise => { +export const getDivisions = async (params?: GetDivisionsParams, options?: RequestInit): Promise => { - return customFetch>(getGetDivisionsUrl(), + return customFetch>(getGetDivisionsUrl(params), { ...options, method: 'GET' @@ -2562,19 +2570,19 @@ export const getDivisions = async ( options?: RequestInit): Promise [`/divisions`] as const; +export const getGetDivisionsKey = (params?: GetDivisionsParams,) => [`/divisions`, ...(params ? [params]: [])] as const; export type GetDivisionsQueryResult = NonNullable>> export type GetDivisionsQueryError = unknown export const useGetDivisions = ( - options?: { swr?:SWRConfiguration>, TError> & { swrKey?: Key, enabled?: boolean }, request?: SecondParameter } + params?: GetDivisionsParams, options?: { swr?:SWRConfiguration>, TError> & { swrKey?: Key, enabled?: boolean }, request?: SecondParameter } ) => { const {swr: swrOptions, request: requestOptions} = options ?? {} const isEnabled = swrOptions?.enabled !== false - const swrKey = swrOptions?.swrKey ?? (() => isEnabled ? getGetDivisionsKey() : null); - const swrFn = () => getDivisions(requestOptions) + const swrKey = swrOptions?.swrKey ?? (() => isEnabled ? getGetDivisionsKey(params) : null); + const swrFn = () => getDivisions(params, requestOptions) const query = useSwr>, TError>(swrKey, swrFn, swrOptions) diff --git a/view/next-project/src/generated/model/index.ts b/view/next-project/src/generated/model/index.ts index 4bfbe7ab..e7763717 100644 --- a/view/next-project/src/generated/model/index.ts +++ b/view/next-project/src/generated/model/index.ts @@ -73,6 +73,7 @@ export * from './getBureausId200'; export * from './getBuyReportsListParams'; export * from './getDepartments200'; export * from './getDepartmentsId200'; +export * from './getDivisionsParams'; export * from './getExpenses200'; export * from './getExpensesDetails200'; export * from './getExpensesDetailsYear200';