Skip to content

Conversation

anatolyshipitz
Copy link
Collaborator

@anatolyshipitz anatolyshipitz commented Aug 11, 2025

  • Integrated QBORepository to fetch effective revenue data, enhancing financial report accuracy.
  • Updated WeeklyFinancialReportRepository to include effective revenue calculations and sorting by marginality levels.
  • Modified report formatting to display effective revenue, margin, and marginality metrics.
  • Added tests to ensure correct sorting of groups by marginality and effective marginality.

These changes improve the financial reporting capabilities, providing clearer insights into revenue and performance metrics.

Summary by CodeRabbit

  • New Features

    • Reports now include top-level and per-project Effective Revenue, plus Effective Margin and Effective Marginality lines. Group headers use a bold name, summaries split into High/Medium/Low and ordered by marginality (alphabetical within a level). Footer shows a dynamic effective-revenue date window; default window increased to 4 months.
  • Tests

    • Added sorting tests and a test-data builder to validate marginality ordering and report formatting.
  • Chores

    • Project records now include a required name field; QBO revenue integration wired into data fetches and tests.

anatolyshipitz and others added 8 commits August 11, 2025 11:43
- Added TypeScript support in ESLint configuration by including `@typescript-eslint/eslint-plugin` and `@typescript-eslint/parser` at version 8.39.0.
- Enhanced ESLint rules with naming conventions for identifiers, interfaces, classes, and constants to improve code quality and consistency.
- Updated `package-lock.json` and `package.json` to reflect the new dependencies and their versions.

These changes strengthen the linting process for TypeScript files and enforce better coding standards across the project.
- Modified ESLint configuration to target only TypeScript files (`**/*.ts`) by removing TypeScript React files (`**/*.tsx`).
- Enhanced naming convention rules to exclude const variables from the default identifier rule and updated the filter for variable naming to ensure uppercase constants are not matched.
- Added `@typescript-eslint/eslint-plugin` and `@typescript-eslint/parser` as devDependencies in `package.json` to support TypeScript linting.

These changes refine the linting process for TypeScript files and improve code quality standards across the project.
- Introduced a new archive file detailing ESLint naming conventions based on the naming-cheatsheet principles to enhance code quality and consistency in the TypeScript codebase.
- Updated `eslint.config.mjs` to implement comprehensive naming rules, including camelCase for constants, PascalCase for classes and enums, and specific prefixes for boolean variables and functions.
- Enhanced the ESLint configuration to allow flexible naming for object properties, MongoDB operators, and dot notation, ensuring compatibility with various coding patterns.
- Updated `package.json` to reflect the latest dependencies and scripts for ESLint.

These changes significantly improve the linting process and enforce better coding standards across the project, promoting maintainability and readability.
- Updated the archive file to include recent enhancements in ESLint naming conventions, specifically enforcing camelCase for boolean variables and functions while maintaining prefix requirements.
- Modified `eslint.config.mjs` to implement camelCase formatting for boolean variables and functions, ensuring better code consistency and readability.
- Added new rules to allow specific patterns for class/model variables and snake_case for parameters, enhancing flexibility in naming conventions.

These changes improve the linting process and enforce stricter naming standards across the TypeScript codebase, promoting maintainability and clarity.
- Revised the archive file to reflect recent updates in ESLint naming conventions, including the transition to PascalCase for boolean variables and functions, and the addition of new function prefixes such as 'setup', 'create', 'init', and 'build'.
- Enhanced `eslint.config.mjs` to implement these changes, ensuring better support for naming patterns and improved variable filtering.
- Introduced a new rule for true constants to use UPPER_CASE formatting, further refining the naming standards.

These updates enhance code quality and consistency across the TypeScript codebase, promoting maintainability and clarity.
- Integrated `QBORepository` to fetch effective revenue data, enhancing financial report accuracy.
- Updated `WeeklyFinancialReportRepository` to include effective revenue calculations and sorting by marginality levels.
- Modified report formatting to display effective revenue, margin, and marginality metrics.
- Added tests to ensure correct sorting of groups by marginality and effective marginality.

These changes improve the financial reporting capabilities, providing clearer insights into revenue and performance metrics.
@anatolyshipitz anatolyshipitz requested a review from killev as a code owner August 11, 2025 17:00
Copy link

coderabbitai bot commented Aug 11, 2025

Warning

Rate limit exceeded

@anatolyshipitz has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 5 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3f5492f and 410826e.

📒 Files selected for processing (4)
  • workers/main/src/services/FinApp/FinAppSchemas.test.ts (1 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (2 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (3 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (5 hunks)

Walkthrough

Integrates QBO effectiveRevenue into data fetch and tests; adds required Project.name across schema/types/repo projections; refactors WeeklyFinancialReport to compute and sort groups by marginality and effective metrics; extends formatter (new effective fields and dynamic footer); adds sorting tests and test data builders.

Changes

Cohort / File(s) Summary
Activity: fetch QBO & augment projects
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts, workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts
Add QBORepository usage; concurrently fetch effectiveRevenue with employees and projects; augment projects with per-project effectiveRevenue; writeJsonFile now includes top-level effectiveRevenue; tests updated to mock QBO and assert new output shape and calls.
QBO config
workers/main/src/configs/qbo.ts
Change effectiveRevenueMonths parsing to use Number() + Math.trunc with fallback to 4 when not finite (replaces previous parseInt fallback of 3).
FinApp schema/types/repo projection
workers/main/src/services/FinApp/FinAppSchemas.ts, workers/main/src/services/FinApp/types.ts, workers/main/src/services/FinApp/FinAppRepository.ts, workers/main/src/services/FinApp/FinAppRepository.test.ts
Add required name to project schema and include name in Mongo projection; update Project type with name: string and optional effectiveRevenue?: number; tests adjusted to include name.
Weekly financial report — formatter API & footer
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts
Rename formatSummaryInputFormatSummaryInput and add lowGroups; extend FormatDetailInput with effectiveRevenue, effectiveMargin, effectiveMarginality; update detail formatting to include Effective Revenue/Margin/Marginality and replace static footer with a dynamic date-window using qboConfig and formatDateToISOString.
Weekly financial report — core refactor & tests
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts, workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts, workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportSorting.test.ts
Replace streaming target-unit flow with pipeline (collectGroupData, processSingleGroup, aggregateGroupData, sortGroupData, createSortedGroups, formatGroupDetails); compute/accumulate effectiveRevenue, effectiveMargin, effectiveMarginality; sort groups by marginality level then alphabetically; add test builders and new sorting tests validating ordering.

Sequence Diagram(s)

sequenceDiagram
  participant Activity as fetchFinancialAppData
  participant FinApp as FinApp API
  participant QBO as QBORepository

  Activity->>FinApp: getEmployees()
  Activity->>FinApp: getProjects()
  Activity->>QBO: getEffectiveRevenue()
  par Concurrency
    FinApp-->>Activity: employees
    FinApp-->>Activity: projects
    QBO-->>Activity: revenueMap
  end
  Activity->>Activity: augment projects with effectiveRevenue by quick_books_id
  Activity-->>Activity: write JSON { employees, projects, effectiveRevenue }
Loading
sequenceDiagram
  participant Repo as WeeklyFinancialReportRepository
  participant Aggregator as GroupAggregator
  participant Calc as MarginalityCalculator
  participant Formatter as WeeklyFinancialReportFormatter
  participant Config as qboConfig

  Repo->>Repo: collectGroupData(targetUnits, employees, projects)
  loop per group
    Repo->>Aggregator: aggregate group units
    Aggregator-->>Repo: totals (cogs, revenue, effectiveRevenue)
    Repo->>Calc: compute marginality
    Calc-->>Repo: marginality {level, indicator, value}
    Repo->>Repo: build GroupData
  end
  Repo->>Repo: sortGroupData (level, groupName)
  Repo->>Formatter: formatDetail(group metrics incl. effectiveRevenue/Margin/Marginality)
  Formatter-->>Repo: detail strings
  Repo->>Formatter: formatFooter(using qboConfig.effectiveRevenueMonths window)
  Formatter-->>Repo: footer string
  Repo-->>Caller: { reportDetails, summary }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • killev

Poem

I thump through ledgers, nibble lines of code,
Counting margins down each rabbit road.
Effective numbers hop into place,
Four months guide the moonlit chase.
Reports arrive — I dance, I bode! 🐇📈

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/add_marginality_level

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@anatolyshipitz anatolyshipitz changed the base branch from main to feature/eslint-naming-conventions August 11, 2025 17:00
Copy link

github-actions bot commented Aug 11, 2025

🔍 Vulnerabilities of temporal-test:latest

📦 Image Reference temporal-test:latest
digestsha256:da30eca7154c7c4cb2616e31f7411021ea9146d5b15c68adf2a57df4c52b164c
vulnerabilitiescritical: 2 high: 7 medium: 0 low: 0
platformlinux/amd64
size218 MB
packages358
📦 Base Image alpine:3
also known as
  • 3.21
  • 3.21.3
  • latest
digestsha256:1c4eef651f65e2f7daee7ee785882ac164b02b78fb74503052a26dc061c90474
vulnerabilitiescritical: 0 high: 0 medium: 0 low: 0
critical: 1 high: 0 medium: 0 low: 0 stdlib 1.23.6 (golang)

pkg:golang/[email protected]

critical : CVE--2025--22871

Affected range<1.23.8
Fixed version1.23.8
EPSS Score0.023%
EPSS Percentile4th percentile
Description

The net/http package improperly accepts a bare LF as a line terminator in chunked data chunk-size lines. This can permit request smuggling if a net/http server is used in conjunction with a server that incorrectly accepts a bare LF as part of a chunk-ext.

critical: 1 high: 0 medium: 0 low: 0 stdlib 1.23.2 (golang)

pkg:golang/[email protected]

critical : CVE--2025--22871

Affected range<1.23.8
Fixed version1.23.8
EPSS Score0.023%
EPSS Percentile4th percentile
Description

The net/http package improperly accepts a bare LF as a line terminator in chunked data chunk-size lines. This can permit request smuggling if a net/http server is used in conjunction with a server that incorrectly accepts a bare LF as part of a chunk-ext.

critical: 0 high: 1 medium: 0 low: 0 github.com/golang-jwt/jwt/v4 4.5.1 (golang)

pkg:golang/github.com/golang-jwt/[email protected]#v4

high 8.7: CVE--2025--30204 Asymmetric Resource Consumption (Amplification)

Affected range<4.5.2
Fixed version4.5.2
CVSS Score8.7
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
EPSS Score0.033%
EPSS Percentile8th percentile
Description

Summary

Function parse.ParseUnverified currently splits (via a call to strings.Split) its argument (which is untrusted data) on periods.

As a result, in the face of a malicious request whose Authorization header consists of Bearer followed by many period characters, a call to that function incurs allocations to the tune of O(n) bytes (where n stands for the length of the function's argument), with a constant factor of about 16. Relevant weakness: CWE-405: Asymmetric Resource Consumption (Amplification)

Details

See parse.ParseUnverified

Impact

Excessive memory allocation

critical: 0 high: 1 medium: 0 low: 0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 0.36.4 (golang)

pkg:golang/go.opentelemetry.io/contrib/[email protected]#google.golang.org/grpc/otelgrpc

high 7.5: CVE--2023--47108 Allocation of Resources Without Limits or Throttling

Affected range<0.46.0
Fixed version0.46.0
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score2.678%
EPSS Percentile85th percentile
Description

Summary

The grpc Unary Server Interceptor opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go

// UnaryServerInterceptor returns a grpc.UnaryServerInterceptor suitable
// for use in a grpc.NewServer call.
func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor {

out of the box adds labels

  • net.peer.sock.addr
  • net.peer.sock.port

that have unbound cardinality. It leads to the server's potential memory exhaustion when many malicious requests are sent.

Details

An attacker can easily flood the peer address and port for requests.

PoC

Apply the attached patch to the example and run the client multiple times. Observe how each request will create a unique histogram and how the memory consumption increases during it.

Impact

In order to be affected, the program has to configure a metrics pipeline, use UnaryServerInterceptor, and does not filter any client IP address and ports via middleware or proxies, etc.

Others

It is similar to already reported vulnerabilities.

Workaround for affected versions

As a workaround to stop being affected, a view removing the attributes can be used.

The other possibility is to disable grpc metrics instrumentation by passing otelgrpc.WithMeterProvider option with noop.NewMeterProvider.

Solution provided by upgrading

In PR #4322, to be released with v0.46.0, the attributes were removed.

References

critical: 0 high: 1 medium: 0 low: 0 golang.org/x/oauth2 0.7.0 (golang)

pkg:golang/golang.org/x/[email protected]

high 7.5: CVE--2025--22868 Improper Validation of Syntactic Correctness of Input

Affected range<0.27.0
Fixed version0.27.0
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score0.076%
EPSS Percentile23rd percentile
Description

An attacker can pass a malicious malformed token which causes unexpected memory to be consumed during parsing.

critical: 0 high: 1 medium: 0 low: 0 golang.org/x/oauth2 0.26.0 (golang)

pkg:golang/golang.org/x/[email protected]

high 7.5: CVE--2025--22868 Improper Validation of Syntactic Correctness of Input

Affected range<0.27.0
Fixed version0.27.0
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score0.076%
EPSS Percentile23rd percentile
Description

An attacker can pass a malicious malformed token which causes unexpected memory to be consumed during parsing.

critical: 0 high: 1 medium: 0 low: 0 c-ares 1.34.3-r0 (apk)

pkg:apk/alpine/[email protected]?os_name=alpine&os_version=3.21

high : CVE--2025--31498

Affected range<1.34.5-r0
Fixed version1.34.5-r0
EPSS Score0.199%
EPSS Percentile42nd percentile
Description
critical: 0 high: 1 medium: 0 low: 0 golang.org/x/crypto 0.32.0 (golang)

pkg:golang/golang.org/x/[email protected]

high : CVE--2025--22869

Affected range<0.35.0
Fixed version0.35.0
EPSS Score0.188%
EPSS Percentile41st percentile
Description

SSH servers which implement file transfer protocols are vulnerable to a denial of service attack from clients which complete the key exchange slowly, or not at all, causing pending content to be read into memory, but never transmitted.

critical: 0 high: 1 medium: 0 low: 0 github.com/golang-jwt/jwt 3.2.2+incompatible (golang)

pkg:golang/github.com/golang-jwt/[email protected]%2Bincompatible

high 8.7: CVE--2025--30204 Asymmetric Resource Consumption (Amplification)

Affected range>=3.2.0
<=3.2.2
Fixed versionNot Fixed
CVSS Score8.7
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
EPSS Score0.033%
EPSS Percentile8th percentile
Description

Summary

Function parse.ParseUnverified currently splits (via a call to strings.Split) its argument (which is untrusted data) on periods.

As a result, in the face of a malicious request whose Authorization header consists of Bearer followed by many period characters, a call to that function incurs allocations to the tune of O(n) bytes (where n stands for the length of the function's argument), with a constant factor of about 16. Relevant weakness: CWE-405: Asymmetric Resource Consumption (Amplification)

Details

See parse.ParseUnverified

Impact

Excessive memory allocation

- Introduced effective financial parameters (Effective Revenue, Effective Margin, Effective Marginality) into the Weekly Financial Reports system via QuickBooks Online integration.
- Updated `WeeklyFinancialReportRepository` to include calculations and sorting by marginality levels.
- Enhanced report formatting to display new metrics and explanations regarding effective revenue calculations.
- Added comprehensive unit tests to validate effective metrics calculations and sorting functionality.

These enhancements improve the accuracy and clarity of financial reporting, providing better insights into revenue and performance metrics.
Base automatically changed from feature/eslint-naming-conventions to main August 12, 2025 14:33
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🔭 Outside diff range comments (1)
workers/main/src/configs/qbo.ts (1)

19-27: Strengthen Zod schema: coerce to number with default and constraints.

Move validation into the schema to ensure a typed, validated value. This keeps config concerns declarative and consistent with other env validation.

Apply this diff:

 export const qboSchema = z.object({
   QBO_API_URL: z.string().url().min(1, 'QBO_API_URL is required'),
   QBO_BEARER_TOKEN: z.string().optional(),
   QBO_CLIENT_ID: z.string().min(1, 'QBO_CLIENT_ID is required'),
   QBO_CLIENT_SECRET: z.string().min(1, 'QBO_CLIENT_SECRET is required'),
   QBO_COMPANY_ID: z.string().min(1, 'QBO_COMPANY_ID is required'),
   QBO_REFRESH_TOKEN: z.string(),
-  QBO_EFFECTIVE_REVENUE_MONTHS: z.string().optional(),
+  QBO_EFFECTIVE_REVENUE_MONTHS: z.coerce
+    .number()
+    .int()
+    .min(1)
+    .max(24)
+    .default(4),
 });

Optionally (outside this hunk), consider parsing process.env with this schema and sourcing qboConfig fields from the parsed object to guarantee type safety end-to-end.

🧹 Nitpick comments (9)
workers/main/src/services/FinApp/types.ts (1)

24-24: Add a short JSDoc for effectiveRevenue to clarify semantics and units.

Please document whether this is gross or net revenue, the currency/units, and the date window (derived from qboConfig.effectiveRevenueMonths). This reduces ambiguity for downstream consumers and tests.

workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts (2)

31-35: Rename effectiveRevenue to clarify it’s a map; avoids confusion with the project field.

The local variable name collides conceptually with the per-project numeric field. Rename to indicate it’s a lookup by QBO customer ref.

Apply this diff:

-    const [employees, projects, effectiveRevenue] = await Promise.all([
+    const [employees, projects, effectiveRevenueByCustomerRef] = await Promise.all([
       repo.getEmployeesByRedmineIds(employeeIds),
       repo.getProjectsByRedmineIds(projectIds),
-      qboRepo.getEffectiveRevenue(),
+      qboRepo.getEffectiveRevenue(),
     ]);

25-26: Consider dependency injection for QBORepository to improve testability.

Instantiating QBORepository inline makes it harder to isolate in unit tests. Passing it as an optional parameter (defaulting to a real instance) mirrors how FinAppRepository could be injected and reduces reliance on module-level mocking.

Example (outside this hunk, for illustration):

export const fetchFinancialAppData = async (
  fileLink: string,
  deps: { finAppRepo?: FinAppRepository; qboRepo?: IQBORepository } = {},
) => {
  const repo = deps.finAppRepo ?? new FinAppRepository();
  const qboRepo = deps.qboRepo ?? new QBORepository();
  ...
};
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportSorting.test.ts (2)

65-89: Strengthen assertions to cover within-level tie-breakers.

Currently you don’t assert ordering between High Group B vs High Group D. Add a case where their effectiveMarginality differs to prove the secondary comparator works as expected within the same level.


73-89: Reduce brittleness of string indexOf checks.

Parsing explicit group lines or returning structured group order from the repository (for test-only path) avoids false positives if names repeat in other sections.

workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (1)

182-200: Also assert ordering in the summary to mirror details ordering.

To catch divergences between detail and summary ordering, add corresponding summary assertions in this test.

workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (3)

17-26: Consider adding JSDoc documentation for the GroupData interface

This new interface introduces several important fields that would benefit from documentation, particularly to clarify the distinction between regular and "effective" metrics.

+/**
+ * Represents aggregated financial data for a group
+ */
 interface GroupData {
+  /** Group identifier name */
   groupName: string;
+  /** Total hours worked by the group */
   groupTotalHours: number;
+  /** Total revenue generated by the group */
   groupTotalRevenue: number;
+  /** Total cost of goods sold for the group */
   groupTotalCogs: number;
+  /** Revenue calculated over the effective period (QBO integration) */
   effectiveRevenue: number;
+  /** Margin calculated using effective revenue */
   effectiveMargin: number;
+  /** Marginality percentage based on effective metrics */
   effectiveMarginality: number;
+  /** Regular marginality calculation result */
   marginality: MarginalityResult;
 }

117-149: Consider renaming function to follow naming conventions

The function name processSingleGroup doesn't follow the established naming pattern. According to the guidelines, it should use the pattern: action + high context + low context.

-  private processSingleGroup(
+  private calculateGroupMetrics(
     targetUnit: TargetUnit,
     targetUnits: TargetUnit[],
     employees: Employee[],
     projects: Project[],
   ): GroupData {

Also update the call site at line 82:

-        const groupDataItem = this.processSingleGroup(
+        const groupDataItem = this.calculateGroupMetrics(

151-166: Simplify function name for better clarity

The function name createSortedGroups is misleading since the groups are already sorted. The function is actually categorizing them by marginality level.

-  private createSortedGroups(groupData: GroupData[]) {
+  private categorizeGroupsByMarginality(groupData: GroupData[]) {

Also update the call site at line 55-56:

     const { highGroups, mediumGroups, lowGroups } =
-      this.createSortedGroups(groupData);
+      this.categorizeGroupsByMarginality(groupData);
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e29aa60 and 084e73c.

⛔ Files ignored due to path filters (1)
  • memory-bank/archive/archive-TargetUnit-Effective-Params-20250811.md is excluded by !memory-bank/**
📒 Files selected for processing (7)
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts (2 hunks)
  • workers/main/src/configs/qbo.ts (1 hunks)
  • workers/main/src/services/FinApp/types.ts (1 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (4 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (3 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (5 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportSorting.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Follow the function naming pattern: prefix? + action (A) + high context (HC) + low context? (LC), using action verbs such as get, fetch, send, create, validate, handle, calculate, and boolean prefixes is, has, should
Use descriptive, unabbreviated variable names; use singular for single values and plural for collections; ensure variable names are context-specific

Files:

  • workers/main/src/services/FinApp/types.ts
  • workers/main/src/configs/qbo.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportSorting.test.ts
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts
workers/main/src/configs/**

📄 CodeRabbit Inference Engine (CLAUDE.md)

workers/main/src/configs/**: Environment validation should be implemented in workers/main/src/configs/
Use Zod schemas for environment validation

Files:

  • workers/main/src/configs/qbo.ts
**/*.test.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

Tests are co-located with source files and should be named with the pattern *.test.ts

Files:

  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportSorting.test.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts
🧬 Code Graph Analysis (6)
workers/main/src/services/FinApp/types.ts (2)
workers/main/src/services/QBO/types.ts (1)
  • IQBORepository (51-53)
workers/main/src/services/WeeklyFinancialReport/IWeeklyFinancialReportRepository.ts (1)
  • GenerateReportInput (4-8)
workers/main/src/configs/qbo.ts (3)
workers/main/src/services/QBO/QBORepository.integration.test.ts (2)
  • it (150-180)
  • qboConfig (13-34)
workers/main/src/services/QBO/QBORepository.errorHandling.test.ts (1)
  • qboConfig (14-35)
workers/main/src/services/QBO/QBORepository.ts (1)
  • calculateDateWindow (149-159)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportSorting.test.ts (1)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (1)
  • WeeklyFinancialReportRepository (28-272)
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts (6)
workers/main/src/services/QBO/QBORepository.ts (2)
  • QBORepository (20-172)
  • getEffectiveRevenue (161-171)
workers/main/src/common/fileUtils.ts (2)
  • readJsonFile (6-17)
  • writeJsonFile (19-32)
workers/main/src/common/types.ts (1)
  • TargetUnit (3-14)
workers/main/src/services/QBO/QBORepository.integration.test.ts (2)
  • invoices (107-147)
  • invoices (61-105)
workers/main/src/services/QBO/types.ts (1)
  • IQBORepository (51-53)
workers/main/src/services/QBO/QBORepository.test.ts (1)
  • it (115-173)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (6)
workers/main/src/services/WeeklyFinancialReport/MarginalityCalculator.ts (2)
  • MarginalityResult (12-17)
  • MarginalityCalculator (19-47)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (1)
  • WeeklyFinancialReportFormatter (32-112)
workers/main/src/common/types.ts (1)
  • TargetUnit (3-14)
workers/main/src/services/FinApp/types.ts (2)
  • Employee (5-12)
  • Project (14-26)
workers/main/src/services/WeeklyFinancialReport/GroupAggregator.ts (1)
  • GroupAggregator (3-15)
workers/main/src/services/WeeklyFinancialReport/IWeeklyFinancialReportRepository.ts (1)
  • AggregateGroupDataInput (16-20)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (5)
workers/main/src/common/formatUtils.ts (1)
  • formatCurrency (1-3)
workers/main/src/configs/qbo.ts (1)
  • qboConfig (3-17)
workers/main/src/common/utils.ts (1)
  • formatDateToISOString (19-25)
workers/main/src/configs/weeklyFinancialReport.ts (2)
  • HIGH_MARGINALITY_THRESHOLD (6-6)
  • MEDIUM_MARGINALITY_THRESHOLD (7-7)
workers/main/src/services/WeeklyFinancialReport/IWeeklyFinancialReportRepository.ts (2)
  • AggregateGroupDataInput (16-20)
  • GenerateReportInput (4-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Docker Security Scanning (n8n, Dockerfile.n8n, n8n-test:latest)
  • GitHub Check: Service Availability Check
🔇 Additional comments (7)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportSorting.test.ts (1)

1-97: Good coverage for level-first, then effectiveMarginality ordering.

The test validates the intended sort: High before Medium before Low, with a secondary order by effectiveMarginality. This protects against regressions in the repository’s sort routine.

workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (1)

5-71: Nice move to test data builders; improves readability and reuse.

createBasicTestData consolidates verbose fixtures and makes tests easier to maintain.

workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (2)

96-115: LGTM! Sorting implementation is correct and well-structured

The two-level sorting strategy (first by marginality level, then by effective marginality within each level) is implemented correctly with clear comments explaining the logic.


168-190: Good separation of concerns in formatting logic

The extraction of formatting logic into a separate method with clear return values improves readability and maintainability.

workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (3)

9-14: Interface naming follows conventions correctly

Good change renaming from formatSummaryInput to FormatSummaryInput to follow TypeScript interface naming conventions.


42-54: Effective metrics integration looks good

The new effective revenue fields are properly integrated into the detail formatting with consistent formatting and clear labeling.


100-111: Footer dynamically adapts to configuration

Good implementation making the footer dynamic based on the qboConfig.effectiveRevenueMonths configuration, providing transparency about the calculation window.

Enhance Weekly Financial Reports with Project Name and Formatting
Adjustments

- Added 'name' field to the project schema to ensure project
identification in financial reports.
- Updated `FinAppRepository` to include project names in the data
retrieval process.
- Removed unnecessary group total hours from report formatting to
streamline output.
- Adjusted formatting in `WeeklyFinancialReportFormatter` for improved
readability and consistency.
- Enhanced sorting logic in `WeeklyFinancialReportRepository` to sort
groups by name alphabetically.

These changes improve the clarity and organization of weekly financial
reports, providing better insights into project performance.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (1)
workers/main/src/services/FinApp/FinAppSchemas.ts (1)

27-31: Require Project.name: update schema, interface, and tests

We’ve made name a required field on the Project schema—let’s align types and tests, and add trimming to avoid accidental whitespace.

• In workers/main/src/services/FinApp/FinAppSchemas.ts, update the name field:

- name: { type: String, required: true },
+ name: { type: String, required: true, trim: true },

• In workers/main/src/services/FinApp/types.ts, add name to the Project interface:

export interface Project {
  name: string;
  redmine_id: number;
  quick_books_id?: number;
  history?: History;
  effectiveRevenue?: number;
  [key: string]: unknown;
}

• In workers/main/src/services/FinApp/FinAppSchemas.test.ts, include a name value in the “should accept valid project” case:

it('should accept valid project', async () => {
  const doc = new ProjectModel({
    name: 'Test Project',
    redmine_id: 456,
    quick_books_id: 789,
    history: { rate: { '2024-01-01': 200 } },
  });
  const err = await validateDoc(doc);
  expect(err).toBeNull();
});
♻️ Duplicate comments (3)
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts (2)

37-46: Tests must assert the new written shape (projects with effectiveRevenue + top-level effectiveRevenue)

The success-case in fetchFinancialAppData.test.ts still asserts only employees and projects. Update it to match the new payload by either augmenting mockProjects or asserting via a mapping.

Example adjustment in the test:

expect(writeJsonFile).toHaveBeenCalledWith(expectedFilename, {
  employees: mockEmployees,
  projects: mockProjects.map(p => ({
    ...p,
    effectiveRevenue:
      p.quick_books_id != null
        ? mockEffectiveRevenue[String(p.quick_books_id)]?.totalAmount ?? 0
        : 0,
  })),
  effectiveRevenue: mockEffectiveRevenue,
});

Optional script to find the failing assertion:

#!/bin/bash
rg -n "writeJsonFile\\).*\\{[\\s\\S]*employees:.*projects:" workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts -A 10

37-46: Use nullish check and string key when indexing effective revenue; avoid falsy-ID bug

Indexing a string-keyed map with a number and using a truthiness check will mis-handle 0-like IDs. Use an explicit nullish check and convert the key to string. Also avoid overshadowing the top-level effectiveRevenue property by using a distinct local variable name.

-    await writeJsonFile(filename, {
-      employees,
-      projects: projects.map((project) => ({
-        ...project,
-        effectiveRevenue: project.quick_books_id
-          ? effectiveRevenue[project.quick_books_id]?.totalAmount || 0
-          : 0,
-      })),
-      effectiveRevenue,
-    });
+    await writeJsonFile(filename, {
+      employees,
+      projects: projects.map((project) => ({
+        ...project,
+        effectiveRevenue:
+          project.quick_books_id != null
+            ? effectiveRevenueByCustomerRef[String(project.quick_books_id)]?.totalAmount ?? 0
+            : 0,
+      })),
+      effectiveRevenue: effectiveRevenueByCustomerRef,
+    });
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (1)

86-96: Harden month subtraction to avoid day overflow (e.g., Mar 31 → Feb 28/29 edge case)

Using setMonth with the same day can overshoot into the following month. Clamp the day to the target month length.

-  private static calculateDateWindow() {
-    const endDate = new Date();
-    const startDate = new Date(endDate);
-
-    startDate.setMonth(endDate.getMonth() - qboConfig.effectiveRevenueMonths);
+  private static calculateDateWindow() {
+    const endDate = new Date();
+    // Compute target year/month by subtracting months safely
+    const monthsToSubtract = qboConfig.effectiveRevenueMonths;
+    const endYear = endDate.getFullYear();
+    const endMonth = endDate.getMonth(); // 0-based
+    const targetMonthIndex = endMonth - monthsToSubtract;
+    const targetYear =
+      endYear + Math.floor(targetMonthIndex / 12); // negative months roll back years
+    const normalizedTargetMonth = ((targetMonthIndex % 12) + 12) % 12;
+    // Clamp day to end of target month
+    const daysInTargetMonth = new Date(
+      targetYear,
+      normalizedTargetMonth + 1,
+      0,
+    ).getDate();
+    const clampedDay = Math.min(endDate.getDate(), daysInTargetMonth);
+    const startDate = new Date(
+      targetYear,
+      normalizedTargetMonth,
+      clampedDay,
+      0,
+      0,
+      0,
+      0,
+    );
 
     return {
       startDate: formatDateToISOString(startDate),
       endDate: formatDateToISOString(endDate),
     };
   }
🧹 Nitpick comments (4)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (3)

248-248: Translate code comment to English

Avoid non-English inline comments for consistency.

-    const processedProjects = new Set<number>(); // Отслеживаем обработанные проекты
+    const processedProjects = new Set<number>(); // Track processed projects to avoid double-counting

251-256: Avoid O(n^2) lookups by pre-indexing employees and projects by redmine_id

Repeated Array.find calls inside the loop are quadratic. Precompute maps for O(1) access.

Outside this function, pre-index once:

const employeeById = new Map(employees.map(e => [e.redmine_id, e]));
const projectById = new Map(projects.map(p => [p.redmine_id, p]));

Then inside the loop:

const employee = employeeById.get(unit.user_id);
const project = projectById.get(unit.project_id);

260-263: Potential cross-group double-counting of project effectiveRevenue

Within a group you dedupe by project, which is good. If the same project appears in multiple groups, its full effectiveRevenue will be added to each group, inflating totals across the report.

If projects can span multiple groups in the input targetUnits, consider allocating project.effectiveRevenue proportionally per group, e.g.:

  • Compute per-project hours by group.
  • Distribute project.effectiveRevenue across groups using hours share.
  • Sum the allocated amount in each group.

Do projects in your weekly input belong to exactly one group? If not, we should adjust the allocation.

workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (1)

74-78: Minor formatting nit: stray leading space before :large_yellow_circle:

There's an extra leading space before the medium-level label that makes alignment inconsistent.

-      summary += ` :large_yellow_circle:  *Marginality is between ${MEDIUM_MARGINALITY_THRESHOLD}-${HIGH_MARGINALITY_THRESHOLD}%*:\n`;
+      summary += `:large_yellow_circle:  *Marginality is between ${MEDIUM_MARGINALITY_THRESHOLD}-${HIGH_MARGINALITY_THRESHOLD}%*:\n`;
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 084e73c and 3a3d9be.

📒 Files selected for processing (5)
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts (2 hunks)
  • workers/main/src/services/FinApp/FinAppRepository.ts (1 hunks)
  • workers/main/src/services/FinApp/FinAppSchemas.ts (1 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (2 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Follow the function naming pattern: prefix? + action (A) + high context (HC) + low context? (LC), using action verbs such as get, fetch, send, create, validate, handle, calculate, and boolean prefixes is, has, should
Use descriptive, unabbreviated variable names; use singular for single values and plural for collections; ensure variable names are context-specific

Files:

  • workers/main/src/services/FinApp/FinAppRepository.ts
  • workers/main/src/services/FinApp/FinAppSchemas.ts
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts
🧬 Code Graph Analysis (5)
workers/main/src/services/FinApp/FinAppRepository.ts (2)
workers/main/src/services/FinApp/types.ts (1)
  • Project (14-25)
workers/main/src/services/FinApp/IFinAppRepository.ts (1)
  • IFinAppRepository (6-15)
workers/main/src/services/FinApp/FinAppSchemas.ts (2)
workers/main/src/services/FinApp/FinAppSchemas.test.ts (1)
  • it (80-106)
workers/main/src/services/FinApp/types.ts (1)
  • Project (14-25)
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts (6)
workers/main/src/services/QBO/QBORepository.ts (2)
  • QBORepository (20-172)
  • getEffectiveRevenue (161-171)
workers/main/src/common/fileUtils.ts (2)
  • readJsonFile (6-17)
  • writeJsonFile (19-32)
workers/main/src/common/types.ts (1)
  • TargetUnit (3-14)
workers/main/src/services/QBO/QBORepository.test.ts (2)
  • qboRepository (61-174)
  • it (115-173)
workers/main/src/services/QBO/QBORepository.integration.test.ts (3)
  • invoices (107-147)
  • qboRepository (39-181)
  • invoices (61-105)
workers/main/src/services/QBO/types.ts (1)
  • IQBORepository (51-53)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (4)
workers/main/src/common/formatUtils.ts (1)
  • formatCurrency (1-3)
workers/main/src/configs/weeklyFinancialReport.ts (2)
  • HIGH_MARGINALITY_THRESHOLD (6-6)
  • MEDIUM_MARGINALITY_THRESHOLD (7-7)
workers/main/src/configs/qbo.ts (1)
  • qboConfig (3-17)
workers/main/src/common/utils.ts (1)
  • formatDateToISOString (19-25)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (6)
workers/main/src/services/WeeklyFinancialReport/MarginalityCalculator.ts (2)
  • MarginalityResult (12-17)
  • MarginalityCalculator (19-47)
workers/main/src/services/WeeklyFinancialReport/IWeeklyFinancialReportRepository.ts (3)
  • IWeeklyFinancialReportRepository (10-14)
  • GenerateReportInput (4-8)
  • AggregateGroupDataInput (16-20)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (1)
  • WeeklyFinancialReportFormatter (31-109)
workers/main/src/common/types.ts (1)
  • TargetUnit (3-14)
workers/main/src/services/FinApp/types.ts (2)
  • Employee (5-12)
  • Project (14-26)
workers/main/src/services/WeeklyFinancialReport/GroupAggregator.ts (1)
  • GroupAggregator (3-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Docker Security Scanning (temporal, Dockerfile.temporal, temporal-test:latest)
  • GitHub Check: Docker Security Scanning (n8n, Dockerfile.n8n, n8n-test:latest)
  • GitHub Check: Service Availability Check
🔇 Additional comments (2)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (1)

171-191: Detail formatting integration looks solid

The new fields (effectiveRevenue, effectiveMargin, effectiveMarginality) are correctly passed to the formatter. Nice separation of concerns.

workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (1)

9-14: Formatter input changes look consistent with repository output

Addition of lowGroups and effective fields aligns with the updated repository pipeline and tests.

- Removed alphabetical sorting of high, medium, and low marginality groups in the `WeeklyFinancialReportRepository`.
- Added a comment to clarify the intention of preserving the established order in the group data.

This change enhances the report's clarity by maintaining the original grouping order, improving the overall organization of financial data.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (2)

150-167: Good: preserved computed order in summary lists

Alphabetical re-sorting was removed. This keeps the performance-based ordering produced by sortGroupData intact in the summary.


95-115: Fix comparator: sort within levels by effectiveMarginality (desc), then by groupName

Current implementation sorts by level then alphabetically, which contradicts the PR objective/tests expecting effectiveMarginality ordering within each level. Update the comment and comparator accordingly.

-    // Sort by marginality level (High -> Medium -> Low),
-    // then within each level by groupName alphabetically
+    // Sort by marginality level (High -> Medium -> Low),
+    // then within each level by effectiveMarginality (desc),
+    // then by groupName alphabetically
     groupData.sort((a, b) => {
       const levelComparison =
         levelOrder[b.marginality.level] - levelOrder[a.marginality.level];

       if (levelComparison !== 0) {
         return levelComparison;
       }

-      // Sort by groupName alphabetically within each level
-      return a.groupName.localeCompare(b.groupName);
+      // Next: higher effectiveMarginality first within each level
+      if (b.effectiveMarginality !== a.effectiveMarginality) {
+        return b.effectiveMarginality - a.effectiveMarginality;
+      }
+      // Finally: alphabetical by name for stability
+      return a.groupName.localeCompare(b.groupName);
     });
🧹 Nitpick comments (3)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (3)

237-274: Optional: add a robust fallback for effectiveRevenue when QBO value is missing

Right now, projects lacking effectiveRevenue contribute 0 even if we have rate×hours data. To avoid undercounting, aggregate a per-project placeholder (sum of projectRate×hours across all units for that project within the group), then add either the QBO effectiveRevenue or the placeholder once per project. Still dedupes by project.

   }: AggregateGroupDataInput) {
     let groupTotalCogs = 0;
     let groupTotalRevenue = 0;
-    let effectiveRevenue = 0;
-    const processedProjects = new Set<number>(); // Отслеживаем обработанные проекты
+    let effectiveRevenue = 0;
+    // Track processed projects to avoid double-counting effective revenue per project
+    const processedProjects = new Set<number>();
+    // Aggregate placeholder effective revenue (rate × hours) per project for cases where QBO value is absent
+    const placeholderRevenueByProject = new Map<number, number>();

     for (const unit of groupUnits) {
       const employee = employees.find((e) => e.redmine_id === unit.user_id);
       const project = projects.find((p) => p.redmine_id === unit.project_id);
       const date = unit.spent_on;
       const employeeRate = this.safeGetRate(employee?.history, date);
       const projectRate = this.safeGetRate(project?.history, date);

       groupTotalCogs += employeeRate * unit.total_hours;
       groupTotalRevenue += projectRate * unit.total_hours;

-      if (project && !processedProjects.has(project.redmine_id)) {
-        effectiveRevenue += project.effectiveRevenue || 0;
-        processedProjects.add(project.redmine_id);
-      }
+      if (project) {
+        // Accumulate a per-project placeholder only if QBO effectiveRevenue is missing
+        if (project.effectiveRevenue == null) {
+          const prev = placeholderRevenueByProject.get(project.redmine_id) ?? 0;
+          placeholderRevenueByProject.set(
+            project.redmine_id,
+            prev + projectRate * unit.total_hours,
+          );
+        }
+        processedProjects.add(project.redmine_id);
+      }
     }

+    // Sum effective revenue once per project: prefer QBO value, else use placeholder
+    for (const projectId of processedProjects) {
+      const proj = projects.find((p) => p.redmine_id === projectId);
+      effectiveRevenue +=
+        proj?.effectiveRevenue ?? (placeholderRevenueByProject.get(projectId) ?? 0);
+    }

This keeps totals consistent and avoids skew when some projects lack QBO effective revenue.


70-94: Optional: avoid O(n²) scanning by pre-indexing one representative unit per group

collectGroupData currently iterates targetUnits and re-filters per group later; pre-indexing reduces unnecessary checks and keeps the same behavior by using the first unit per group.

   private collectGroupData(
     targetUnits: TargetUnit[],
     employees: Employee[],
     projects: Project[],
   ) {
-    const processedGroupIds = new Set<number>();
-    const groupData: GroupData[] = [];
-
-    for (const targetUnit of targetUnits) {
-      if (!processedGroupIds.has(targetUnit.group_id)) {
-        processedGroupIds.add(targetUnit.group_id);
-        const groupDataItem = this.processSingleGroup(
-          targetUnit,
-          targetUnits,
-          employees,
-          projects,
-        );
-
-        groupData.push(groupDataItem);
-      }
-    }
-
-    return { groupData };
+    // One representative unit per group_id
+    const firstUnitByGroup = new Map<number, TargetUnit>();
+    for (const unit of targetUnits) {
+      if (!firstUnitByGroup.has(unit.group_id)) {
+        firstUnitByGroup.set(unit.group_id, unit);
+      }
+    }
+    const groupData: GroupData[] = [];
+    for (const unit of firstUnitByGroup.values()) {
+      const groupDataItem = this.processSingleGroup(
+        unit,
+        targetUnits,
+        employees,
+        projects,
+      );
+      groupData.push(groupDataItem);
+    }
+    return { groupData };
   }

212-226: Optional: prevent UTC off-by-one by using a date formatter instead of toISOString().slice(0, 10)

toISOString is UTC; slicing may shift a day depending on timezone. Use the project’s date formatter to produce consistent yyyy-mm-dd strings.

   private composeWeeklyReportTitle(currentDate: Date): string {
     const quarter = Math.floor(currentDate.getMonth() / 3);
-    const periodStart = new Date(currentDate.getFullYear(), quarter * 3, 1)
-      .toISOString()
-      .slice(0, 10);
-    const periodEnd = new Date(
+    const periodStart = formatDateToISOString(
+      new Date(currentDate.getFullYear(), quarter * 3, 1),
+    );
+    const endBound = new Date(
       currentDate.getFullYear(),
       currentDate.getMonth(),
       currentDate.getDate() - ((currentDate.getDay() + 6) % 7) - 1,
-    )
-      .toISOString()
-      .slice(0, 10);
+    );
+    const periodEnd = formatDateToISOString(endBound);

Add import (outside this range):

import { getRateByDate, formatDateToISOString } from '../../common/formatUtils';
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a3d9be and fe3b6c9.

📒 Files selected for processing (1)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Follow the function naming pattern: prefix? + action (A) + high context (HC) + low context? (LC), using action verbs such as get, fetch, send, create, validate, handle, calculate, and boolean prefixes is, has, should
Use descriptive, unabbreviated variable names; use singular for single values and plural for collections; ensure variable names are context-specific

Files:

  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts
🧬 Code Graph Analysis (1)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (6)
workers/main/src/services/WeeklyFinancialReport/MarginalityCalculator.ts (2)
  • MarginalityResult (12-17)
  • MarginalityCalculator (19-47)
workers/main/src/services/WeeklyFinancialReport/IWeeklyFinancialReportRepository.ts (3)
  • IWeeklyFinancialReportRepository (10-14)
  • GenerateReportInput (4-8)
  • AggregateGroupDataInput (16-20)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (3)
  • WeeklyFinancialReportFormatter (31-109)
  • WeeklyFinancialReportFormatter (27-84)
  • FormatDetailInput (14-23)
workers/main/src/common/types.ts (1)
  • TargetUnit (3-14)
workers/main/src/services/FinApp/types.ts (2)
  • Employee (5-12)
  • Project (14-26)
workers/main/src/services/WeeklyFinancialReport/GroupAggregator.ts (1)
  • GroupAggregator (3-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Docker Security Scanning (n8n, Dockerfile.n8n, n8n-test:latest)
  • GitHub Check: Service Availability Check

- Updated the calculation of effectiveRevenueMonths in the qboConfig to ensure it handles non-finite values correctly. The new implementation uses a self-invoking function to parse the environment variable and defaults to 4 if the value is not a valid number.

This change improves the robustness of the configuration by preventing potential errors from invalid input.
- Updated the date calculation in `WeeklyFinancialReportFormatter` to correctly handle month and year transitions when subtracting months from the current date. This ensures that the start date is accurately set to the last day of the target month, improving the reliability of financial report date ranges.
- Cleaned up the code structure for better readability and maintainability.

These changes enhance the accuracy of the date window used in weekly financial reports, providing more reliable data for analysis.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (1)

96-116: Sorting logic doesn't match test expectations.

Based on the test files, groups should be sorted by:

  1. Marginality level (High → Medium → Low)
  2. EffectiveMarginality descending within each level
  3. GroupName alphabetically as the final tiebreaker

The current implementation only sorts by level and then name, missing the effectiveMarginality comparison. This will cause the test "sorts groups by effectiveMarginality in descending order" to fail.

   private sortGroupData(groupData: GroupData[]) {
     const levelOrder = {
       [MarginalityLevel.High]: 3,
       [MarginalityLevel.Medium]: 2,
       [MarginalityLevel.Low]: 1,
     };
 
     // Sort by marginality level (High -> Medium -> Low),
-    // then within each level by groupName alphabetically
+    // then within each level by effectiveMarginality (desc),
+    // then by groupName alphabetically
     groupData.sort((a, b) => {
       const levelComparison =
         levelOrder[b.marginality.level] - levelOrder[a.marginality.level];
 
       if (levelComparison !== 0) {
         return levelComparison;
       }
 
-      // Sort by groupName alphabetically within each level
-      return a.groupName.localeCompare(b.groupName);
+      // Sort by effectiveMarginality descending within each level
+      if (b.effectiveMarginality !== a.effectiveMarginality) {
+        return b.effectiveMarginality - a.effectiveMarginality;
+      }
+      // Finally by groupName alphabetically
+      return a.groupName.localeCompare(b.groupName);
     });
   }
🧹 Nitpick comments (4)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (4)

17-25: Consider making GroupData interface exportable for better testing and modularity.

The GroupData interface is central to this module's data flow. Exporting it would enable type-safe testing of individual components and improve modularity by allowing other components to consume this interface if needed.

-interface GroupData {
+export interface GroupData {
   groupName: string;
   groupTotalRevenue: number;
   groupTotalCogs: number;
   effectiveRevenue: number;
   effectiveMargin: number;
   effectiveMarginality: number;
   marginality: MarginalityResult;
 }

245-245: Fix typo in Russian comment.

The code contains a comment in Russian. For consistency and maintainability in an international codebase, comments should be in English.

-    const processedProjects = new Set<number>(); // Отслеживаем обработанные проекты
+    const processedProjects = new Set<number>(); // Track processed projects

118-149: Consider extracting aggregation logic for improved testability.

The processSingleGroup method handles multiple responsibilities: fetching group units, aggregating data, and calculating marginality. Consider splitting the aggregation logic into a separate, testable method.

Consider refactoring to improve testability:

 private processSingleGroup(
   targetUnit: TargetUnit,
   targetUnits: TargetUnit[],
   employees: Employee[],
   projects: Project[],
 ): GroupData {
   const { groupUnits } = GroupAggregator.aggregateGroup(
     targetUnits,
     targetUnit.group_id,
   );
+  return this.createGroupData(
+    targetUnit.group_name,
+    groupUnits,
+    employees,
+    projects,
+  );
+ }
+
+ private createGroupData(
+   groupName: string,
+   groupUnits: TargetUnit[],
+   employees: Employee[],
+   projects: Project[],
+ ): GroupData {
   const {
     groupTotalCogs,
     groupTotalRevenue,
     effectiveRevenue,
     effectiveMargin,
     effectiveMarginality,
   } = this.aggregateGroupData({ groupUnits, employees, projects });
   const marginality = MarginalityCalculator.calculate(
     groupTotalRevenue,
     groupTotalCogs,
   );
 
   return {
-    groupName: targetUnit.group_name,
+    groupName,
     groupTotalRevenue,
     groupTotalCogs,
     effectiveRevenue,
     effectiveMargin,
     effectiveMarginality,
     marginality,
   };
 }

71-94: Consider using Map for O(1) lookups instead of Set.

The method correctly deduplicates groups, but using a Map would be more idiomatic and allow direct access to previously processed groups if needed in the future.

 private collectGroupData(
   targetUnits: TargetUnit[],
   employees: Employee[],
   projects: Project[],
 ) {
-  const processedGroupIds = new Set<number>();
+  const processedGroups = new Map<number, GroupData>();
   const groupData: GroupData[] = [];
 
   for (const targetUnit of targetUnits) {
-    if (!processedGroupIds.has(targetUnit.group_id)) {
-      processedGroupIds.add(targetUnit.group_id);
+    if (!processedGroups.has(targetUnit.group_id)) {
       const groupDataItem = this.processSingleGroup(
         targetUnit,
         targetUnits,
         employees,
         projects,
       );
-
+      processedGroups.set(targetUnit.group_id, groupDataItem);
       groupData.push(groupDataItem);
     }
   }
 
   return { groupData };
 }
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf413a0 and c57099e.

📒 Files selected for processing (2)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (2 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Follow the function naming pattern: prefix? + action (A) + high context (HC) + low context? (LC), using action verbs such as get, fetch, send, create, validate, handle, calculate, and boolean prefixes is, has, should
Use descriptive, unabbreviated variable names; use singular for single values and plural for collections; ensure variable names are context-specific

Files:

  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts
🧬 Code Graph Analysis (1)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (6)
workers/main/src/services/WeeklyFinancialReport/MarginalityCalculator.ts (2)
  • MarginalityResult (12-17)
  • MarginalityCalculator (19-47)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (1)
  • WeeklyFinancialReportFormatter (31-136)
workers/main/src/common/types.ts (1)
  • TargetUnit (3-14)
workers/main/src/services/FinApp/types.ts (2)
  • Employee (5-12)
  • Project (14-26)
workers/main/src/services/WeeklyFinancialReport/GroupAggregator.ts (1)
  • GroupAggregator (3-15)
workers/main/src/services/WeeklyFinancialReport/IWeeklyFinancialReportRepository.ts (1)
  • AggregateGroupDataInput (16-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Docker Security Scanning (n8n, Dockerfile.n8n, n8n-test:latest)
  • GitHub Check: Service Availability Check
🔇 Additional comments (1)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (1)

257-260: Good implementation of effective revenue aggregation with deduplication.

The logic correctly:

  • Uses the QBO-provided effectiveRevenue field from the project
  • Implements deduplication using a Set to prevent double-counting when a project appears multiple times
  • Falls back to 0 when effectiveRevenue is undefined

- Updated the `fetchFinancialAppData` function to utilize the `QBORepository` for fetching effective revenue data, enhancing the accuracy of financial reports.
- Adjusted the handling of effective revenue in the report generation process to ensure it correctly maps to project IDs.
- Added necessary mocks for `QBORepository` in the corresponding test file to ensure proper testing of the new functionality.

These changes improve the integration of financial data from QuickBooks Online, providing more reliable insights into project performance.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (1)
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts (1)

202-221: Add missing error case: QBO getEffectiveRevenue failure should surface as AppError.

You covered readJsonFile, getEmployees, getProjects, and writeJsonFile failures. Include the QBO path to ensure parity and prevent regressions when QBO is unavailable.

Proposed test to add within "error cases":

it('throws AppError when getEffectiveRevenue throws', async () => {
  readJsonFile.mockResolvedValue(mockTargetUnits);
  qboRepoInstance.getEffectiveRevenue.mockRejectedValue(new Error('fail-qbo'));
  await expectAppError(
    fetchFinancialAppData(fileLink),
    'Failed to get Fin App Data',
  );
});

I can open a follow-up PR to include this test if you prefer.

🧹 Nitpick comments (3)
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts (3)

98-102: Optional: Strengthen typing around QBO repo mock to catch API drift.

Right now qboRepoInstance is typed with a loose shape. Consider importing and using IQBORepository (if exported) to type the mock, or at least hoist a typed getEffectiveRevenueMock for better IDE help. Not blocking.


131-135: QBORepository mock wiring is fine; consider resetting mock implementations.

Using vi.clearAllMocks resets call state but preserves implementations. Given you set implementations in beforeEach, this is safe; however, vi.resetAllMocks in afterEach can further reduce coupling between tests by also resetting implementations.


136-140: Inline qboRepoInstance factory is fine.

This approach is straightforward. If you plan to assert calls, consider hoisting getEffectiveRevenueMock as a separate variable to assert on it explicitly.

📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c57099e and 24c143d.

📒 Files selected for processing (2)
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts (7 hunks)
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.test.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

Tests are co-located with source files and should be named with the pattern *.test.ts

Files:

  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Follow the function naming pattern: prefix? + action (A) + high context (HC) + low context? (LC), using action verbs such as get, fetch, send, create, validate, handle, calculate, and boolean prefixes is, has, should
Use descriptive, unabbreviated variable names; use singular for single values and plural for collections; ensure variable names are context-specific

Files:

  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts
🧬 Code Graph Analysis (1)
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts (5)
workers/main/src/services/QBO/types.ts (1)
  • CustomerRevenueByRef (1-7)
workers/main/src/services/QBO/QBORepository.ts (1)
  • QBORepository (20-172)
workers/main/src/services/FinApp/IFinAppRepository.ts (1)
  • IFinAppRepository (6-15)
workers/main/src/services/FinApp/FinAppRepository.ts (1)
  • FinAppRepository (6-32)
workers/main/src/services/QBO/QBORepository.integration.test.ts (2)
  • invoices (107-147)
  • invoices (61-105)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Docker Security Scanning (n8n, Dockerfile.n8n, n8n-test:latest)
  • GitHub Check: Service Availability Check
🔇 Additional comments (5)
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts (5)

8-8: Type-only FinApp imports are correct and scoped.

Importing Employee, IFinAppRepository, and Project as types is precise and avoids runtime coupling.


10-11: QBO type import and module import look correct.

CustomerRevenueByRef aligns with the effective revenue map shape used across QBORepository.


34-36: QBO module mocked appropriately.

Providing a constructor mock for QBORepository is the right seam for injecting the test double.


61-68: Effective revenue fixture shape is valid.

The test data matches CustomerRevenueByRef and uses string keys as expected by QBO aggregation.


117-117: Success-mock of getEffectiveRevenue is in the right place.

Stubbing the QBO call keeps the test focused on orchestration and output shaping.

…nancial Reports

- Reformatted import statements in `fetchFinancialAppData.test.ts` for better readability.
- Adjusted error handling in `fetchFinancialAppData` to ensure proper disconnection of the mongo pool.
- Enhanced test data in `WeeklyFinancialReportRepository.test.ts` to include effective revenue calculations for projects, improving the accuracy of marginality assessments.

These changes enhance the clarity and reliability of financial report tests, ensuring more accurate calculations and better organization of test data.
- Modified the `FinAppRepository.test.ts` to include the 'name' field in the projection for project data retrieval, enhancing the accuracy of test expectations.
- Added a documentation comment for the 'name' field in the `Project` interface within `types.ts`, clarifying its purpose in the project schema.

These changes improve the clarity and reliability of tests related to project data, ensuring that the project name is correctly handled in the repository logic.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (2)
workers/main/src/services/FinApp/FinAppRepository.test.ts (1)

3-17: Mocking after imports can cause the repo to use real models — hoist vi.mock before importing FinAppRepository

Current order imports FinAppRepository (which imports FinAppSchemas) before calling vi.mock. Depending on transform/hoisting, this can silently bind the real models inside the repository and make tests flaky or hit the DB unintentionally. Hoist the vi.mock call above the FinAppRepository import.

Apply this diff to ensure mocks are applied before the repo is loaded:

 import { beforeEach, describe, expect, it, vi } from 'vitest';
 
 import { FinAppRepositoryError } from '../../common/errors';
-import { FinAppRepository } from './FinAppRepository';
-import { EmployeeModel, ProjectModel } from './FinAppSchemas';
-
-vi.mock('./FinAppSchemas', () => {
-  return {
-    EmployeeModel: {
-      find: vi.fn(),
-    },
-    ProjectModel: {
-      find: vi.fn(),
-    },
-  };
-});
+vi.mock('./FinAppSchemas', () => ({
+  EmployeeModel: {
+    find: vi.fn(),
+  },
+  ProjectModel: {
+    find: vi.fn(),
+  },
+}));
+
+import { FinAppRepository } from './FinAppRepository';
+import { EmployeeModel, ProjectModel } from './FinAppSchemas';
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts (1)

217-224: Add test case for QBO repository failure

The test suite covers failures for reading files, fetching employees, fetching projects, and writing files, but doesn't test what happens when the QBO repository fails to fetch effective revenue.

Add this test case after the existing error cases:

     it('throws AppError when writeJsonFile throws', async () => {
       readJsonFile.mockResolvedValue(mockTargetUnits);
       writeJsonFile.mockRejectedValue(new Error('fail-write'));
       await expectAppError(
         fetchFinancialAppData(fileLink),
         'Failed to get Fin App Data',
       );
     });
+
+    it('throws AppError when getEffectiveRevenue throws', async () => {
+      readJsonFile.mockResolvedValue(mockTargetUnits);
+      qboRepoInstance.getEffectiveRevenue.mockRejectedValue(
+        new Error('fail-qbo-revenue'),
+      );
+      await expectAppError(
+        fetchFinancialAppData(fileLink),
+        'Failed to get Fin App Data',
+      );
+    });
♻️ Duplicate comments (3)
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts (3)

179-183: Verify that connect and disconnect are actually called

The current test only checks that calling disconnect doesn't throw, but doesn't verify that the cleanup actually happens during the workflow execution.

Apply this diff to properly verify the cleanup behavior:

     it('always disconnects the mongo pool', async () => {
       setupSuccessMocks();
       await fetchFinancialAppData(fileLink).catch(() => {});
-      expect(() => mongoPoolInstance.disconnect()).not.toThrow();
+      expect(connect).toHaveBeenCalledTimes(1);
+      expect(disconnect).toHaveBeenCalledTimes(1);
     });

60-60: Fix type mismatch: quick_books_id should be a number

Based on the Project interface definition and FinAppRepository projection, quick_books_id is typed as number | undefined. Using a string '10' here will cause TypeScript errors and may break revenue lookups that expect numeric keys.

Apply this diff to fix the type mismatch:

-    quick_books_id: '10',
+    quick_books_id: 10,

169-176: Strengthen test assertions to verify QBO integration

The test should verify that the QBO repository was properly instantiated and its method was called to ensure the integration path is fully validated.

Apply this diff to enhance the test coverage:

       expect(writeJsonFile).toHaveBeenCalledWith(expectedFilename, {
         employees: mockEmployees,
         projects: [
           {
             ...mockProjects[0],
             effectiveRevenue: 5000,
           },
         ],
         effectiveRevenue: mockEffectiveRevenue,
       });
+      expect(QBORepository).toHaveBeenCalledTimes(1);
+      expect(qboRepoInstance.getEffectiveRevenue).toHaveBeenCalledTimes(1);
🧹 Nitpick comments (1)
workers/main/src/services/FinApp/FinAppRepository.test.ts (1)

95-95: Align test descriptions with method names for clarity

Using the exact method names in the describe blocks improves discoverability and consistency.

-describe('getEmployees', () => {
+describe('getEmployeesByRedmineIds', () => {
...
-describe('getProjects', () => {
+describe('getProjectsByRedmineIds', () => {

Also applies to: 115-115

📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24c143d and 000890b.

📒 Files selected for processing (5)
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts (7 hunks)
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts (2 hunks)
  • workers/main/src/services/FinApp/FinAppRepository.test.ts (1 hunks)
  • workers/main/src/services/FinApp/types.ts (2 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.ts
  • workers/main/src/services/FinApp/types.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.test.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

Tests are co-located with source files and should be named with the pattern *.test.ts

Files:

  • workers/main/src/services/FinApp/FinAppRepository.test.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Follow the function naming pattern: prefix? + action (A) + high context (HC) + low context? (LC), using action verbs such as get, fetch, send, create, validate, handle, calculate, and boolean prefixes is, has, should
Use descriptive, unabbreviated variable names; use singular for single values and plural for collections; ensure variable names are context-specific

Files:

  • workers/main/src/services/FinApp/FinAppRepository.test.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts
  • workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts
🧬 Code Graph Analysis (3)
workers/main/src/services/FinApp/FinAppRepository.test.ts (1)
workers/main/src/services/FinApp/FinAppRepository.ts (1)
  • FinAppRepository (6-32)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (1)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (2)
  • WeeklyFinancialReportRepository (27-275)
  • WeeklyFinancialReportRepository (30-199)
workers/main/src/activities/weeklyFinancialReports/fetchFinancialAppData.test.ts (4)
workers/main/src/services/QBO/types.ts (1)
  • CustomerRevenueByRef (1-7)
workers/main/src/services/QBO/QBORepository.ts (1)
  • QBORepository (20-172)
workers/main/src/services/FinApp/IFinAppRepository.ts (1)
  • IFinAppRepository (6-15)
workers/main/src/services/FinApp/FinAppRepository.ts (1)
  • FinAppRepository (6-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker Security Scanning (n8n, Dockerfile.n8n, n8n-test:latest)
🔇 Additional comments (5)
workers/main/src/services/FinApp/FinAppRepository.test.ts (1)

121-124: No action required: projection already includes ‘name’.

The getProjectsByRedmineIds method in FinAppRepository.ts already projects { 'name': 1, 'redmine_id': 1, 'quick_books_id': 1, 'history.rate': 1 }, matching the test’s assertion. No code or test changes are needed.

Likely an incorrect or invalid review comment.

workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (4)

5-71: LGTM! Excellent refactoring with data builders.

The introduction of createBasicTestData() improves test maintainability by centralizing test data creation and making it reusable across multiple test cases. The data structure is comprehensive and well-organized.


73-132: Well-designed marginality test data with clear mathematical documentation.

The createMarginalityTestData() function provides focused test fixtures specifically for marginality sorting validation. The inline comments clearly explain the mathematical calculations for achieving target marginalities (50%, 75%, 67%), making the test intentions explicit and verifiable.


137-181: Test updated correctly to use data builder pattern.

The existing test has been properly refactored to use the new createBasicTestData() builder while maintaining all its original assertions. The test comprehensively validates both summary and details output, including proper formatting, currency display, and marginality indicators.


198-216: Comprehensive test for effectiveMarginality sorting behavior.

The test effectively validates that groups are sorted by effectiveMarginality in descending order (Group B: 75% → Group C: 67% → Group A: 50%). The approach of checking index positions in the details string is appropriate for verifying the sort order in the formatted output.

- Updated `WeeklyFinancialReportRepository.test.ts` to enhance test data by including project names and adjusting the sorting logic to ensure groups are sorted alphabetically within the same marginality level.
- Modified `WeeklyFinancialReportSorting.test.ts` to reflect the new sorting criteria, ensuring that groups are sorted by marginality level and then by group name alphabetically.
- Cleaned up the `WeeklyFinancialReportRepository.ts` interface implementation for better readability.

These changes improve the accuracy and clarity of the weekly financial report tests, ensuring proper sorting and organization of group data.
- Added a 'name' field to the project data in `fetchFinancialAppData.test.ts` to improve test accuracy.
- Changed the `quick_books_id` type from string to number for consistency in `fetchFinancialAppData.test.ts`.
- Cleaned up the implementation of the `WeeklyFinancialReportRepository` class for better readability.

These changes enhance the clarity and reliability of the weekly financial report tests and improve the overall structure of the repository code.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (2)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (2)

236-260: Speed up lookups with pre-indexed maps

Currently, each unit performs O(n) searches over employees and projects. Build maps once to reduce lookups to O(1). This can significantly improve performance for larger inputs.

Apply this diff:

   private aggregateGroupData({
     groupUnits,
     employees,
     projects,
   }: AggregateGroupDataInput) {
     let groupTotalCogs = 0;
     let groupTotalRevenue = 0;
     let effectiveRevenue = 0;
-    const processedProjects = new Set<number>(); // Отслеживаем обработанные проекты
+    const processedProjects = new Set<number>(); // Track processed projects to avoid double-counting
+    const employeesById = new Map(employees.map((e) => [e.redmine_id, e]));
+    const projectsById = new Map(projects.map((p) => [p.redmine_id, p]));
 
     for (const unit of groupUnits) {
-      const employee = employees.find((e) => e.redmine_id === unit.user_id);
-      const project = projects.find((p) => p.redmine_id === unit.project_id);
+      const employee = employeesById.get(unit.user_id);
+      const project = projectsById.get(unit.project_id);
       const date = unit.spent_on;
       const employeeRate = this.safeGetRate(employee?.history, date);
       const projectRate = this.safeGetRate(project?.history, date);
 
       groupTotalCogs += employeeRate * unit.total_hours;
       groupTotalRevenue += projectRate * unit.total_hours;
 
       if (project && !processedProjects.has(project.redmine_id)) {
         effectiveRevenue += project.effectiveRevenue || 0;
         processedProjects.add(project.redmine_id);
       }
     }

211-225: Ensure date formatting isn’t affected by UTC offsets

Using toISOString().slice(0, 10) always formats in UTC, which can shift the date by one day depending on the server’s timezone. We should replace it with a helper that formats in local time (or a fixed timezone) to guarantee the intended YYYY-MM-DD output.

• Location:

  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (around lines 211–225)

• Suggested helper (e.g., at the top of the file or in a shared util):

/**
 * Formats a Date as 'YYYY-MM-DD' in the specified timeZone (default: system local).
 */
function formatDate(
  date: Date,
  timeZone: string = Intl.DateTimeFormat().resolvedOptions().timeZone
): string {
  return new Intl.DateTimeFormat('en-CA', { timeZone }).format(date);
}

• Example diff in composeWeeklyReportTitle:

-    const periodStart = new Date(
-      currentDate.getFullYear(),
-      quarter * 3,
-      1
-    ).toISOString()
-     .slice(0, 10);
+    const periodStart = formatDate(
+      new Date(currentDate.getFullYear(), quarter * 3, 1)
+    );

-    const periodEnd = new Date(
-      currentDate.getFullYear(),
-      currentDate.getMonth(),
-      currentDate.getDate() - ((currentDate.getDay() + 6) % 7) - 1
-    )
-      .toISOString()
-      .slice(0, 10);
+    const periodEnd = formatDate(
+      new Date(
+        currentDate.getFullYear(),
+        currentDate.getMonth(),
+        currentDate.getDate() - ((currentDate.getDay() + 6) % 7) - 1
+      )
+    );

This eliminates any unexpected one-day shifts by using a reliable formatting approach.

♻️ Duplicate comments (1)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (1)

95-115: Optional: refine within-level sort by effectiveMarginality, then name

If you want higher-performing groups to surface within each marginality level, compare by effectiveMarginality (desc) before groupName. Coordinate with tests if you adopt this.

-    // Sort by marginality level (High -> Medium -> Low),
-    // then within each level by groupName alphabetically
+    // Sort by marginality level (High -> Medium -> Low),
+    // then within each level by effectiveMarginality (desc),
+    // then by groupName alphabetically
     groupData.sort((a, b) => {
       const levelComparison =
         levelOrder[b.marginality.level] - levelOrder[a.marginality.level];

       if (levelComparison !== 0) {
         return levelComparison;
       }

-      // Sort by groupName alphabetically within each level
-      return a.groupName.localeCompare(b.groupName);
+      if (b.effectiveMarginality !== a.effectiveMarginality) {
+        return b.effectiveMarginality - a.effectiveMarginality;
+      }
+      return a.groupName.localeCompare(b.groupName);
     });
🧹 Nitpick comments (2)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (1)

89-90: Initialize repository per test to avoid accidental shared state

Although the repository appears stateless, constructing it once at describe-scope can hide state-related regressions later. Initialize in beforeEach for isolation.

Apply this diff:

-import { describe, expect, it } from 'vitest';
+import { describe, expect, it, beforeEach } from 'vitest';
@@
-describe('WeeklyFinancialReportRepository', () => {
-  const repo = new WeeklyFinancialReportRepository();
+describe('WeeklyFinancialReportRepository', () => {
+  let repo: WeeklyFinancialReportRepository;
+  beforeEach(() => {
+    repo = new WeeklyFinancialReportRepository();
+  });
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (1)

51-53: Ensure visual separation between details and footer

Concatenating without a newline risks footer sticking to the last group. Add a line break for readability.

-    const reportDetails =
-      initialDetails + WeeklyFinancialReportFormatter.formatFooter();
+    const reportDetails = `${initialDetails}\n${WeeklyFinancialReportFormatter.formatFooter()}`;
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 000890b and b768575.

📒 Files selected for processing (3)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (3 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (4 hunks)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportSorting.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportSorting.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.test.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

Tests are co-located with source files and should be named with the pattern *.test.ts

Files:

  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Follow the function naming pattern: prefix? + action (A) + high context (HC) + low context? (LC), using action verbs such as get, fetch, send, create, validate, handle, calculate, and boolean prefixes is, has, should
Use descriptive, unabbreviated variable names; use singular for single values and plural for collections; ensure variable names are context-specific

Files:

  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts
  • workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts
🧬 Code Graph Analysis (2)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (1)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (1)
  • WeeklyFinancialReportRepository (27-274)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (6)
workers/main/src/services/WeeklyFinancialReport/MarginalityCalculator.ts (2)
  • MarginalityResult (12-17)
  • MarginalityCalculator (19-47)
workers/main/src/services/WeeklyFinancialReport/IWeeklyFinancialReportRepository.ts (3)
  • IWeeklyFinancialReportRepository (10-14)
  • GenerateReportInput (4-8)
  • AggregateGroupDataInput (16-20)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportFormatter.ts (3)
  • WeeklyFinancialReportFormatter (31-136)
  • formatSummaryInput (7-12)
  • WeeklyFinancialReportFormatter (27-84)
workers/main/src/common/types.ts (1)
  • TargetUnit (3-14)
workers/main/src/services/FinApp/types.ts (2)
  • Employee (5-12)
  • Project (14-30)
workers/main/src/services/WeeklyFinancialReport/GroupAggregator.ts (1)
  • GroupAggregator (3-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Docker Security Scanning (n8n, Dockerfile.n8n, n8n-test:latest)
  • GitHub Check: Service Availability Check
🔇 Additional comments (4)
workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.test.ts (2)

5-87: Good builder: clear, reusable fixtures

The createBasicTestData helper is tidy and reads well. It covers target units, employees, and projects with required fields (including project.name) and keeps tests DRY.


153-228: Alphabetical ordering test is appropriate and focused

The scenario cleanly isolates “within-level” ordering and uses consistent inputs to keep all groups at the same marginality level. Assertion via indexOf is straightforward and effective.

workers/main/src/services/WeeklyFinancialReport/WeeklyFinancialReportRepository.ts (2)

54-56: Preserving order in summary looks good

createSortedGroups now respects the global order computed by sortGroupData. This aligns the summary with the detailed section ordering.


243-259: Correct: use QBO effectiveRevenue and dedupe per project

Using project.effectiveRevenue (with per-project de-duping) aligns with the PR objective and avoids double-counting across multiple units for the same project.

- Renamed `QBORepository` to `qboRepository` for consistency in naming conventions within `fetchFinancialAppData.test.ts`.
- Updated mock implementations to reflect the new variable name, ensuring clarity and maintainability in the test structure.
- Added assertions to verify the call counts for `qboRepository` and its method `getEffectiveRevenue`, enhancing test reliability.

These changes improve the readability and accuracy of the financial app data tests, ensuring proper mocking and verification of the QBO repository interactions.
- Updated the handling of `quick_books_id` in `fetchFinancialAppData` to ensure it is converted to a string before accessing effective revenue data, preventing potential type-related issues.
- Adjusted error handling in the test for `fetchFinancialAppData` to maintain proper disconnection of the mongo pool, ensuring resource management is correctly implemented.

These changes enhance the reliability of financial app data fetching and improve the robustness of the associated tests.
- Added `groupTotalHours` to the `FormatDetailInput` interface and updated the `WeeklyFinancialReportFormatter` to include total hours in the formatted output.
- Modified the `WeeklyFinancialReportRepository` to aggregate and return `groupTotalHours` alongside other financial metrics.

These changes improve the detail and comprehensiveness of the weekly financial reports, providing better insights into group performance.
@anatolyshipitz anatolyshipitz enabled auto-merge (squash) August 13, 2025 15:03
- Added a 'name' field to the project model in `FinAppSchemas.test.ts` to ensure valid project creation in tests.
- Adjusted assertions in `WeeklyFinancialReportRepository.test.ts` to standardize the casing of output strings, improving test accuracy and consistency.

These changes enhance the reliability of tests related to project data and weekly financial reports, ensuring that outputs are correctly formatted and validated.
@anatolyshipitz anatolyshipitz self-assigned this Aug 13, 2025
Copy link

Copy link
Contributor

@killev killev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good

@anatolyshipitz anatolyshipitz merged commit 66653e3 into main Aug 13, 2025
22 of 26 checks passed
@anatolyshipitz anatolyshipitz deleted the feature/add_marginality_level branch August 13, 2025 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants