@managemint-solutions/sdk 0.42.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -202,10 +202,13 @@ await supabase.tasks.archive(task.mms_id);
202
202
  await supabase.tasks.unarchive(task.mms_id);
203
203
  await supabase.tasks.remove(task.mms_id); // soft delete
204
204
  await supabase.tasks.restore(task.mms_id);
205
+ await supabase.tasks.linkTask(task.mms_id, { linked_task_id: otherId, link_type: TaskLinkType.CHILD });
206
+ await supabase.tasks.unlinkTask(task.mms_id, otherId);
205
207
  ```
206
208
 
207
209
  A task's `task_id` (its per-organization number) is assigned by the database on insert, and each
208
- task carries its embedded `status`, `assigned_to`, `project` and `client`.
210
+ task carries its embedded `status`, `assigned_to`, `project` and `client`. The detail read also carries its `parent`,
211
+ `subtasks` and `related` tasks, read from `task_links` in both directions.
209
212
 
210
213
  The client decodes `{ mms_id, organization_id }` from the JWT itself and scopes every query
211
214
  by `organization_id`. `supabase.supabase` is the raw `@supabase/supabase-js` client
@@ -15,9 +15,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.AdditionalDetailsResource = void 0;
18
- const enum_1 = require("@managemint-solutions/entities/audit-trail/enum");
18
+ const enum_1 = require("@managemint-solutions/entities/additional-details/enum");
19
+ const enum_2 = require("@managemint-solutions/entities/audit-trail/enum");
19
20
  const audit_1 = require("../audit");
20
21
  const errors_1 = require("../errors");
22
+ const won_1 = require("../leads/won");
21
23
  const values_1 = require("./values");
22
24
  __exportStar(require("./dto"), exports);
23
25
  __exportStar(require("./values"), exports);
@@ -41,7 +43,7 @@ class AdditionalDetailsResource {
41
43
  this.auth = auth;
42
44
  this.table = (0, audit_1.auditedTable)(supabase, auth, audit, {
43
45
  table: 'additional_details',
44
- entityType: enum_1.AuditTrailEntityType.ADDITIONAL_DETAILS,
46
+ entityType: enum_2.AuditTrailEntityType.ADDITIONAL_DETAILS,
45
47
  });
46
48
  }
47
49
  async getById(additionalDetailsId) {
@@ -68,6 +70,9 @@ class AdditionalDetailsResource {
68
70
  return data.map(withCastValue);
69
71
  }
70
72
  async create(entity, entityId, input) {
73
+ if (entity === enum_1.AdditionalDetailsEntityTypes.LEAD) {
74
+ await (0, won_1.refuseWonLead)(this.supabase, this.auth.organization_id, entityId);
75
+ }
71
76
  const row = await this.table.insert({
72
77
  organization_id: this.auth.organization_id,
73
78
  entity_type: entity,
package/dist/client.d.ts CHANGED
@@ -9,6 +9,7 @@ import { NotificationsResource } from './notifications';
9
9
  import { BillingReadsResource } from './billing';
10
10
  import { InvoicesReadsResource } from './billing/invoices';
11
11
  import { LabelsResource } from './labels';
12
+ import { LeadsResource } from './leads';
12
13
  import { LeaveEntitlementsResource, LeaveRequestsResource, LeaveTypesResource } from './leave';
13
14
  import { OrganizationsResource } from './organizations';
14
15
  import { PermissionsResource } from './permissions';
@@ -58,6 +59,7 @@ export declare class SupabaseClient {
58
59
  readonly permissions: PermissionsResource;
59
60
  readonly users: UsersResource;
60
61
  readonly supportingFiles: SupportingFilesResource;
62
+ readonly leads: LeadsResource;
61
63
  readonly clients: ClientsResource;
62
64
  readonly projects: ProjectsResource;
63
65
  readonly tasks: TasksResource;
package/dist/client.js CHANGED
@@ -11,6 +11,7 @@ const notifications_1 = require("./notifications");
11
11
  const billing_1 = require("./billing");
12
12
  const invoices_1 = require("./billing/invoices");
13
13
  const labels_1 = require("./labels");
14
+ const leads_1 = require("./leads");
14
15
  const leave_1 = require("./leave");
15
16
  const organizations_1 = require("./organizations");
16
17
  const permissions_1 = require("./permissions");
@@ -43,6 +44,7 @@ class SupabaseClient {
43
44
  permissions;
44
45
  users;
45
46
  supportingFiles;
47
+ leads;
46
48
  clients;
47
49
  projects;
48
50
  tasks;
@@ -71,6 +73,8 @@ class SupabaseClient {
71
73
  this.users = new users_1.UsersResource(this.supabase, this.auth, this.audit);
72
74
  this.supportingFiles = new supporting_files_1.SupportingFilesResource(this.supabase, this.auth, this.audit);
73
75
  this.clients = new clients_1.ClientsResource(this.supabase, this.auth, this.audit);
76
+ // After clients and additional details: a convert writes through both.
77
+ this.leads = new leads_1.LeadsResource(this.supabase, this.auth, this.audit, this.additionalDetails);
74
78
  this.projects = new projects_1.ProjectsResource(this.supabase, this.auth, this.audit);
75
79
  this.tasks = new tasks_1.TasksResource(this.supabase, this.auth, this.audit);
76
80
  this.leaveTypes = new leave_1.LeaveTypesResource(this.supabase, this.auth, this.audit);
@@ -34,7 +34,8 @@ const CLIENT_LIST_SELECT = `
34
34
  label,
35
35
  is_complete,
36
36
  is_fail,
37
- is_triage
37
+ is_triage,
38
+ is_won
38
39
  ),
39
40
  created_by:user_profiles!created_by (*)
40
41
  `;
@@ -50,7 +51,8 @@ const CLIENT_DETAIL_SELECT = `
50
51
  colour,
51
52
  is_complete,
52
53
  is_fail,
53
- is_triage
54
+ is_triage,
55
+ is_won
54
56
  ),
55
57
  contract_client,
56
58
  contracted_hours,
package/dist/index.d.ts CHANGED
@@ -35,8 +35,9 @@ export { AddUserDto, GetUsersQueryDto, UpdateMyProfileDto, UpdateUserDto, UserId
35
35
  export type { ClaimOutcome, WebhookEventRow, WebhookEventsTable } from './webhook-events';
36
36
  export { GetSupportingFilesDto, SupportingFileIdDto } from './supporting-files';
37
37
  export { ClientIdDto, CreateClientDto, GetClientsDto, UpdateClientDto } from './clients';
38
+ export { ConvertLeadDto, CreateLeadDto, GetLeadsDto, LeadIdDto, UpdateLeadDto } from './leads';
38
39
  export { CreateProjectDto, GetProjectsDto, ProjectIdDto, UpdateProjectDto } from './projects';
39
- export { CreateTaskDto, GetTasksDto, TaskIdDto, UpdateTaskDto } from './tasks';
40
+ export { AddTaskLinkDto, CreateTaskDto, GetTasksDto, TaskIdDto, TaskLinkIdDto, UpdateTaskDto } from './tasks';
40
41
  export type { CreateLeaveEntitlementInput, CreateLeaveRequestRow, LeaveEntitlementCover, ReviewLeaveRequestRow, UpdateLeaveRequestRow, } from './leave';
41
42
  export { CreateLeaveEntitlementDto, CreateLeaveRequestDto, CreateLeaveTypeDto, GetLeaveEntitlementsDto, GetLeaveRequestsDto, GetLeaveTypesDto, LeaveEntitlementIdDto, LeaveRequestIdDto, LeaveTypeIdDto, ReviewLeaveRequestDto, UpdateLeaveEntitlementDto, UpdateLeaveRequestDto, UpdateLeaveTypeDto, } from './leave';
42
43
  export { GetPublicHolidaysDto } from './public-holidays';
package/dist/index.js CHANGED
@@ -8,7 +8,8 @@
8
8
  // to keep. The row and input types they produce and accept are exported for annotations.
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.AddUserDto = exports.UpdatePermissionsDto = exports.PermissionsResource = exports.UpdateOrganizationDto = exports.CreateOrganizationDto = exports.NotificationIdDto = exports.GetNotificationsDto = exports.NoteIdDto = exports.GetNotesDto = exports.AddNoteDto = exports.UpdateAdditionalDetailsDto = exports.IsAdditionalDetailsValueByType = exports.GetAdditionalDetailsDto = exports.AdditionalDetailsIdDto = exports.AddAdditionalDetailsDto = exports.UpdateLabelDto = exports.LabelIdDto = exports.GetLabelsDto = exports.AddLabelDto = exports.UpdateStatusDto = exports.StatusIdDto = exports.GetStatusesDto = exports.AddStatusDto = exports.CreateSupportRequestDto = exports.UpdateConfigDto = exports.CreateConfigDto = exports.CreateFeatureRequestDto = exports.InvoiceIdDto = exports.GetInvoicesQueryDto = exports.ReinstateSubscriptionDto = exports.PurchaseModuleDto = exports.PaymentIdDto = exports.InitialPaymentDto = exports.GetPaymentsQueryDto = exports.CancelSubscriptionDto = exports.GetAuditTrailDto = exports.IsNullable = exports.EmptyToNull = exports.SupabaseClientError = exports.createSupabaseServiceClient = exports.SupabaseServiceClient = exports.SignInDto = exports.SendPasswordResetEmailDto = exports.ResetPasswordDto = exports.ExchangeTokenHashDto = exports.ExchangeRefreshTokenDto = exports.createSupabaseAuthClient = exports.SupabaseAuthClient = exports.createSupabaseClient = exports.SupabaseClient = void 0;
11
- exports.monthlyTotalMinor = exports.weekStartOf = exports.minutesBetween = exports.UpdateTimesheetEntryDto = exports.TimesheetIdDto = exports.TimesheetEntryIdDto = exports.ReviewTimesheetDto = exports.GetTimesheetsDto = exports.GetTimesheetReportDto = exports.CreateTimesheetEntryDto = exports.todayInJohannesburg = exports.shiftDays = exports.leavePeriodFor = exports.isWithinPeriod = exports.countWorkingDays = exports.addOneYearMinusDay = exports.GetPublicHolidaysDto = exports.UpdateLeaveTypeDto = exports.UpdateLeaveRequestDto = exports.UpdateLeaveEntitlementDto = exports.ReviewLeaveRequestDto = exports.LeaveTypeIdDto = exports.LeaveRequestIdDto = exports.LeaveEntitlementIdDto = exports.GetLeaveTypesDto = exports.GetLeaveRequestsDto = exports.GetLeaveEntitlementsDto = exports.CreateLeaveTypeDto = exports.CreateLeaveRequestDto = exports.CreateLeaveEntitlementDto = exports.UpdateTaskDto = exports.TaskIdDto = exports.GetTasksDto = exports.CreateTaskDto = exports.UpdateProjectDto = exports.ProjectIdDto = exports.GetProjectsDto = exports.CreateProjectDto = exports.UpdateClientDto = exports.GetClientsDto = exports.CreateClientDto = exports.ClientIdDto = exports.SupportingFileIdDto = exports.GetSupportingFilesDto = exports.UserIdDto = exports.UpdateUserDto = exports.UpdateMyProfileDto = exports.GetUsersQueryDto = void 0;
11
+ exports.TimesheetEntryIdDto = exports.ReviewTimesheetDto = exports.GetTimesheetsDto = exports.GetTimesheetReportDto = exports.CreateTimesheetEntryDto = exports.todayInJohannesburg = exports.shiftDays = exports.leavePeriodFor = exports.isWithinPeriod = exports.countWorkingDays = exports.addOneYearMinusDay = exports.GetPublicHolidaysDto = exports.UpdateLeaveTypeDto = exports.UpdateLeaveRequestDto = exports.UpdateLeaveEntitlementDto = exports.ReviewLeaveRequestDto = exports.LeaveTypeIdDto = exports.LeaveRequestIdDto = exports.LeaveEntitlementIdDto = exports.GetLeaveTypesDto = exports.GetLeaveRequestsDto = exports.GetLeaveEntitlementsDto = exports.CreateLeaveTypeDto = exports.CreateLeaveRequestDto = exports.CreateLeaveEntitlementDto = exports.UpdateTaskDto = exports.TaskLinkIdDto = exports.TaskIdDto = exports.GetTasksDto = exports.CreateTaskDto = exports.AddTaskLinkDto = exports.UpdateProjectDto = exports.ProjectIdDto = exports.GetProjectsDto = exports.CreateProjectDto = exports.UpdateLeadDto = exports.LeadIdDto = exports.GetLeadsDto = exports.CreateLeadDto = exports.ConvertLeadDto = exports.UpdateClientDto = exports.GetClientsDto = exports.CreateClientDto = exports.ClientIdDto = exports.SupportingFileIdDto = exports.GetSupportingFilesDto = exports.UserIdDto = exports.UpdateUserDto = exports.UpdateMyProfileDto = exports.GetUsersQueryDto = void 0;
12
+ exports.monthlyTotalMinor = exports.weekStartOf = exports.minutesBetween = exports.UpdateTimesheetEntryDto = exports.TimesheetIdDto = void 0;
12
13
  var client_1 = require("./client");
13
14
  Object.defineProperty(exports, "SupabaseClient", { enumerable: true, get: function () { return client_1.SupabaseClient; } });
14
15
  Object.defineProperty(exports, "createSupabaseClient", { enumerable: true, get: function () { return client_1.createSupabaseClient; } });
@@ -94,15 +95,23 @@ Object.defineProperty(exports, "ClientIdDto", { enumerable: true, get: function
94
95
  Object.defineProperty(exports, "CreateClientDto", { enumerable: true, get: function () { return clients_1.CreateClientDto; } });
95
96
  Object.defineProperty(exports, "GetClientsDto", { enumerable: true, get: function () { return clients_1.GetClientsDto; } });
96
97
  Object.defineProperty(exports, "UpdateClientDto", { enumerable: true, get: function () { return clients_1.UpdateClientDto; } });
98
+ var leads_1 = require("./leads");
99
+ Object.defineProperty(exports, "ConvertLeadDto", { enumerable: true, get: function () { return leads_1.ConvertLeadDto; } });
100
+ Object.defineProperty(exports, "CreateLeadDto", { enumerable: true, get: function () { return leads_1.CreateLeadDto; } });
101
+ Object.defineProperty(exports, "GetLeadsDto", { enumerable: true, get: function () { return leads_1.GetLeadsDto; } });
102
+ Object.defineProperty(exports, "LeadIdDto", { enumerable: true, get: function () { return leads_1.LeadIdDto; } });
103
+ Object.defineProperty(exports, "UpdateLeadDto", { enumerable: true, get: function () { return leads_1.UpdateLeadDto; } });
97
104
  var projects_1 = require("./projects");
98
105
  Object.defineProperty(exports, "CreateProjectDto", { enumerable: true, get: function () { return projects_1.CreateProjectDto; } });
99
106
  Object.defineProperty(exports, "GetProjectsDto", { enumerable: true, get: function () { return projects_1.GetProjectsDto; } });
100
107
  Object.defineProperty(exports, "ProjectIdDto", { enumerable: true, get: function () { return projects_1.ProjectIdDto; } });
101
108
  Object.defineProperty(exports, "UpdateProjectDto", { enumerable: true, get: function () { return projects_1.UpdateProjectDto; } });
102
109
  var tasks_1 = require("./tasks");
110
+ Object.defineProperty(exports, "AddTaskLinkDto", { enumerable: true, get: function () { return tasks_1.AddTaskLinkDto; } });
103
111
  Object.defineProperty(exports, "CreateTaskDto", { enumerable: true, get: function () { return tasks_1.CreateTaskDto; } });
104
112
  Object.defineProperty(exports, "GetTasksDto", { enumerable: true, get: function () { return tasks_1.GetTasksDto; } });
105
113
  Object.defineProperty(exports, "TaskIdDto", { enumerable: true, get: function () { return tasks_1.TaskIdDto; } });
114
+ Object.defineProperty(exports, "TaskLinkIdDto", { enumerable: true, get: function () { return tasks_1.TaskLinkIdDto; } });
106
115
  Object.defineProperty(exports, "UpdateTaskDto", { enumerable: true, get: function () { return tasks_1.UpdateTaskDto; } });
107
116
  var leave_1 = require("./leave");
108
117
  Object.defineProperty(exports, "CreateLeaveEntitlementDto", { enumerable: true, get: function () { return leave_1.CreateLeaveEntitlementDto; } });
@@ -0,0 +1,35 @@
1
+ import type { UUID } from 'crypto';
2
+ import type { ConvertLeadDto as ConvertLeadDtoType, CreateLeadDto as CreateLeadDtoType, GetLeadsDto as GetLeadsDtoType, LeadIdDto as LeadIdDtoType, UpdateLeadDto as UpdateLeadDtoType } from '@managemint-solutions/entities/leads/dto';
3
+ import { LeadSource } from '@managemint-solutions/entities/leads/enum';
4
+ export declare class GetLeadsDto implements Omit<GetLeadsDtoType, 'status_id_in'> {
5
+ readonly search?: string | null;
6
+ readonly page: number;
7
+ readonly page_size: number;
8
+ readonly created_after?: string | null;
9
+ readonly created_before?: string | null;
10
+ readonly status_id_in?: string[] | null;
11
+ readonly status_id_not_in?: string[] | null;
12
+ readonly assigned_to_id?: string | null;
13
+ readonly completed?: boolean | null;
14
+ readonly archive: boolean;
15
+ }
16
+ export declare class LeadIdDto implements LeadIdDtoType {
17
+ readonly leadId: string;
18
+ }
19
+ export declare class CreateLeadDto implements CreateLeadDtoType {
20
+ readonly name: string;
21
+ readonly contact_person?: string | null;
22
+ readonly email?: string | null;
23
+ readonly phone?: string | null;
24
+ readonly status_id?: UUID | null;
25
+ readonly assigned_to_id?: string | null;
26
+ readonly source?: LeadSource | null;
27
+ readonly estimated_value_minor?: number | null;
28
+ readonly follow_up_date?: string | null;
29
+ readonly lost_reason?: string | null;
30
+ }
31
+ export declare class UpdateLeadDto extends CreateLeadDto implements UpdateLeadDtoType {
32
+ }
33
+ export declare class ConvertLeadDto implements ConvertLeadDtoType {
34
+ readonly client_status_id: string | null;
35
+ }
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ConvertLeadDto = exports.UpdateLeadDto = exports.CreateLeadDto = exports.LeadIdDto = exports.GetLeadsDto = void 0;
13
+ const class_validator_1 = require("class-validator");
14
+ const class_transformer_1 = require("class-transformer");
15
+ const enum_1 = require("@managemint-solutions/entities/leads/enum");
16
+ const validators_1 = require("../validators");
17
+ // class-validator versions of the shared DTO types from @managemint-solutions/entities, so the
18
+ // wire contract cannot drift. `status_id` is typed as the entities' `UUID | null` for the same
19
+ // reason; class-validator only sees the runtime string.
20
+ // `status_id_in` is omitted from the implemented type on purpose: the entities type carries the
21
+ // CSV wire value (`string | null`), while this class carries the `@Transform`-parsed
22
+ // `string[] | null` the query builder feeds to `.in('status_id', ...)`. `status_id_not_in` is not
23
+ // in the entities type at all: it is what the api resolves `completed: false` into, as for tasks.
24
+ class GetLeadsDto {
25
+ search;
26
+ page = 1;
27
+ page_size = 10;
28
+ created_after;
29
+ created_before;
30
+ status_id_in;
31
+ status_id_not_in;
32
+ assigned_to_id;
33
+ // Tri-state: true = only won leads, false = only open ones, omitted = no completion filter.
34
+ // The api resolves it to status ids before it reaches `list`.
35
+ completed;
36
+ archive = false;
37
+ }
38
+ exports.GetLeadsDto = GetLeadsDto;
39
+ __decorate([
40
+ (0, class_validator_1.IsOptional)(),
41
+ (0, validators_1.IsNullable)(),
42
+ (0, class_validator_1.IsString)({ message: 'Search query must be a string' }),
43
+ (0, class_validator_1.MinLength)(1, { message: 'Search query cannot be empty' }),
44
+ (0, class_validator_1.MaxLength)(255, { message: 'Search query cannot exceed 255 characters' }),
45
+ __metadata("design:type", Object)
46
+ ], GetLeadsDto.prototype, "search", void 0);
47
+ __decorate([
48
+ (0, class_validator_1.IsOptional)(),
49
+ (0, class_transformer_1.Transform)(({ value }) => value ? Number(value) : 1, { toClassOnly: true }),
50
+ (0, class_validator_1.IsNumber)({}, { message: 'Page must be a number' }),
51
+ (0, class_validator_1.Min)(1, { message: 'Page must be at least 1' }),
52
+ __metadata("design:type", Number)
53
+ ], GetLeadsDto.prototype, "page", void 0);
54
+ __decorate([
55
+ (0, class_validator_1.IsOptional)(),
56
+ (0, class_transformer_1.Transform)(({ value }) => value ? Number(value) : 10, { toClassOnly: true }),
57
+ (0, class_validator_1.IsNumber)({}, { message: 'Page size must be a number' }),
58
+ (0, class_validator_1.Min)(1, { message: 'Page size must be at least 1' }),
59
+ (0, class_validator_1.Max)(100, { message: 'Page size cannot exceed 100' }),
60
+ __metadata("design:type", Number)
61
+ ], GetLeadsDto.prototype, "page_size", void 0);
62
+ __decorate([
63
+ (0, class_validator_1.IsOptional)(),
64
+ (0, validators_1.IsNullable)(),
65
+ (0, class_validator_1.IsDateString)({}, { message: 'Start date must be a valid ISO 8601 date string' }),
66
+ __metadata("design:type", Object)
67
+ ], GetLeadsDto.prototype, "created_after", void 0);
68
+ __decorate([
69
+ (0, class_validator_1.IsOptional)(),
70
+ (0, validators_1.IsNullable)(),
71
+ (0, class_validator_1.IsDateString)({}, { message: 'End date must be a valid ISO 8601 date string' }),
72
+ __metadata("design:type", Object)
73
+ ], GetLeadsDto.prototype, "created_before", void 0);
74
+ __decorate([
75
+ (0, class_validator_1.IsOptional)(),
76
+ (0, validators_1.IsNullable)(),
77
+ (0, class_transformer_1.Transform)(({ value }) => value ? value.split(',').map((id) => id.trim()) : null, { toClassOnly: true }),
78
+ (0, class_validator_1.IsUUID)('7', { each: true, message: 'Each status ID must be a valid UUID v7' }),
79
+ __metadata("design:type", Object)
80
+ ], GetLeadsDto.prototype, "status_id_in", void 0);
81
+ __decorate([
82
+ (0, class_validator_1.IsOptional)(),
83
+ (0, validators_1.IsNullable)(),
84
+ (0, class_transformer_1.Transform)(({ value }) => value ? value.split(',').map((id) => id.trim()) : null, { toClassOnly: true }),
85
+ (0, class_validator_1.IsUUID)('7', { each: true, message: 'Each status ID must be a valid UUID v7' }),
86
+ __metadata("design:type", Object)
87
+ ], GetLeadsDto.prototype, "status_id_not_in", void 0);
88
+ __decorate([
89
+ (0, class_validator_1.IsOptional)(),
90
+ (0, validators_1.IsNullable)()
91
+ // v4: the owner is a `users.mms_id`, the GoTrue auth uid (see GetTasksDto).
92
+ ,
93
+ (0, class_validator_1.IsUUID)('4', { message: 'Invalid assigned to ID format' }),
94
+ __metadata("design:type", Object)
95
+ ], GetLeadsDto.prototype, "assigned_to_id", void 0);
96
+ __decorate([
97
+ (0, class_validator_1.IsOptional)(),
98
+ (0, validators_1.IsNullable)(),
99
+ (0, class_transformer_1.Transform)(({ value }) => value === 'true' ? true : value === 'false' ? false : value, { toClassOnly: true }),
100
+ (0, class_validator_1.IsBoolean)({ message: 'Completed must be true or false' }),
101
+ __metadata("design:type", Object)
102
+ ], GetLeadsDto.prototype, "completed", void 0);
103
+ __decorate([
104
+ (0, class_validator_1.IsOptional)(),
105
+ (0, validators_1.IsNullable)(),
106
+ (0, class_transformer_1.Transform)(({ value }) => value ? value === 'true' : false, { toClassOnly: true }),
107
+ (0, class_validator_1.IsBoolean)({ message: 'Include archived must be a boolean' }),
108
+ __metadata("design:type", Boolean)
109
+ ], GetLeadsDto.prototype, "archive", void 0);
110
+ class LeadIdDto {
111
+ leadId;
112
+ }
113
+ exports.LeadIdDto = LeadIdDto;
114
+ __decorate([
115
+ (0, class_validator_1.IsUUID)('7', { message: 'Invalid lead ID format' }),
116
+ __metadata("design:type", String)
117
+ ], LeadIdDto.prototype, "leadId", void 0);
118
+ class CreateLeadDto {
119
+ name;
120
+ contact_person;
121
+ email;
122
+ phone;
123
+ status_id;
124
+ // Omitted defaults to the creator. v4: a `users.mms_id` is the GoTrue auth uid.
125
+ assigned_to_id;
126
+ // The only check on the value: `leads.source` is plain text.
127
+ source;
128
+ estimated_value_minor;
129
+ follow_up_date;
130
+ // Required when the status is a fail status and cleared otherwise - `LeadsResource` decides.
131
+ lost_reason;
132
+ }
133
+ exports.CreateLeadDto = CreateLeadDto;
134
+ __decorate([
135
+ (0, class_validator_1.IsString)({ message: 'Invalid name' }),
136
+ (0, class_validator_1.MinLength)(1, { message: 'Name cannot be empty' }),
137
+ (0, class_validator_1.MaxLength)(100, { message: 'Name cannot exceed 100 characters' }),
138
+ __metadata("design:type", String)
139
+ ], CreateLeadDto.prototype, "name", void 0);
140
+ __decorate([
141
+ (0, class_validator_1.IsOptional)(),
142
+ (0, validators_1.IsNullable)(),
143
+ (0, class_validator_1.IsString)({ message: 'Invalid contact person' }),
144
+ (0, class_validator_1.MaxLength)(100, { message: 'Contact person cannot exceed 100 characters' }),
145
+ __metadata("design:type", Object)
146
+ ], CreateLeadDto.prototype, "contact_person", void 0);
147
+ __decorate([
148
+ (0, class_validator_1.IsOptional)(),
149
+ (0, validators_1.IsNullable)(),
150
+ (0, class_validator_1.ValidateIf)((o) => o.email !== ''),
151
+ (0, class_validator_1.IsEmail)({}, { message: 'Invalid email address' }),
152
+ __metadata("design:type", Object)
153
+ ], CreateLeadDto.prototype, "email", void 0);
154
+ __decorate([
155
+ (0, class_validator_1.IsOptional)(),
156
+ (0, validators_1.IsNullable)(),
157
+ (0, class_validator_1.IsString)({ message: 'Invalid phone number' }),
158
+ __metadata("design:type", Object)
159
+ ], CreateLeadDto.prototype, "phone", void 0);
160
+ __decorate([
161
+ (0, class_validator_1.IsOptional)(),
162
+ (0, validators_1.IsNullable)(),
163
+ (0, class_validator_1.IsUUID)('7', { message: 'Invalid status ID format' }),
164
+ __metadata("design:type", Object)
165
+ ], CreateLeadDto.prototype, "status_id", void 0);
166
+ __decorate([
167
+ (0, class_validator_1.IsOptional)(),
168
+ (0, validators_1.IsNullable)(),
169
+ (0, class_validator_1.IsUUID)('4', { message: 'Invalid assigned to ID format' }),
170
+ __metadata("design:type", Object)
171
+ ], CreateLeadDto.prototype, "assigned_to_id", void 0);
172
+ __decorate([
173
+ (0, class_validator_1.IsOptional)(),
174
+ (0, validators_1.IsNullable)(),
175
+ (0, class_validator_1.IsEnum)(enum_1.LeadSource, { message: 'Invalid lead source' }),
176
+ __metadata("design:type", Object)
177
+ ], CreateLeadDto.prototype, "source", void 0);
178
+ __decorate([
179
+ (0, class_validator_1.IsOptional)(),
180
+ (0, validators_1.IsNullable)(),
181
+ (0, class_validator_1.IsInt)({ message: 'Estimated value must be a whole number of minor units' }),
182
+ (0, class_validator_1.Min)(0, { message: 'Estimated value must be at least 0' }),
183
+ __metadata("design:type", Object)
184
+ ], CreateLeadDto.prototype, "estimated_value_minor", void 0);
185
+ __decorate([
186
+ (0, class_validator_1.IsOptional)(),
187
+ (0, validators_1.IsNullable)(),
188
+ (0, validators_1.IsCalendarDate)({ message: 'Follow-up date must be a calendar date (YYYY-MM-DD)' }),
189
+ __metadata("design:type", Object)
190
+ ], CreateLeadDto.prototype, "follow_up_date", void 0);
191
+ __decorate([
192
+ (0, class_validator_1.IsOptional)(),
193
+ (0, validators_1.IsNullable)(),
194
+ (0, class_validator_1.IsString)({ message: 'Invalid lost reason' }),
195
+ (0, class_validator_1.MaxLength)(500, { message: 'Lost reason cannot exceed 500 characters' }),
196
+ __metadata("design:type", Object)
197
+ ], CreateLeadDto.prototype, "lost_reason", void 0);
198
+ class UpdateLeadDto extends CreateLeadDto {
199
+ }
200
+ exports.UpdateLeadDto = UpdateLeadDto;
201
+ class ConvertLeadDto {
202
+ client_status_id;
203
+ }
204
+ exports.ConvertLeadDto = ConvertLeadDto;
205
+ __decorate([
206
+ (0, validators_1.IsNullable)(),
207
+ (0, class_validator_1.IsUUID)('7', { message: 'Invalid status ID format' }),
208
+ __metadata("design:type", Object)
209
+ ], ConvertLeadDto.prototype, "client_status_id", void 0);
@@ -0,0 +1,54 @@
1
+ import type { ClientEntity } from '@managemint-solutions/entities/clients';
2
+ import type { MmsList } from '@managemint-solutions/entities/common';
3
+ import type { LeadEntity, LeadSummary } from '@managemint-solutions/entities/leads';
4
+ import type { AuthContext } from '../auth';
5
+ import type { AdditionalDetailsResource } from '../additional-details';
6
+ import { type AuditResource } from '../audit';
7
+ import type { ClientsResource } from '../clients';
8
+ import type { TypedSupabaseClient } from '../client';
9
+ import type { ConvertLeadDto, CreateLeadDto, GetLeadsDto, UpdateLeadDto } from './dto';
10
+ export * from './dto';
11
+ export declare class LeadsResource {
12
+ private readonly supabase;
13
+ private readonly auth;
14
+ private readonly audit;
15
+ private readonly additionalDetails;
16
+ private readonly table;
17
+ constructor(supabase: TypedSupabaseClient, auth: AuthContext, audit: AuditResource, additionalDetails: AdditionalDetailsResource);
18
+ list(query: GetLeadsDto): Promise<MmsList<LeadSummary>>;
19
+ getById(leadId: string): Promise<LeadEntity>;
20
+ create(input: CreateLeadDto): Promise<LeadEntity>;
21
+ update(leadId: string, input: UpdateLeadDto): Promise<LeadEntity>;
22
+ archive(leadId: string): Promise<LeadEntity>;
23
+ unarchive(leadId: string): Promise<LeadEntity>;
24
+ restore(leadId: string): Promise<LeadEntity>;
25
+ /** Soft delete - the row stays, flagged `deleted`, and `restore()` brings it back. A Won lead cannot be deleted. */
26
+ remove(leadId: string): Promise<void>;
27
+ /**
28
+ * Turns the lead into a client: the client is created from the lead's name, email and phone
29
+ * with the status the caller picked, the lead's contact person, source and estimated value
30
+ * become additional details on it, the lead's notes, additional details and files move to it,
31
+ * and the lead moves to Won with `converted_client_id` and `converted_at` set. It is not
32
+ * deleted - it stays as a record of the deal, and from then on it cannot be changed
33
+ * (`refuseWonLead`). The list hides Won leads by default.
34
+ *
35
+ * There is no transaction across these writes (PostgREST and Storage cannot share one). A
36
+ * failure after the client is created leaves that client and a lead that is still not
37
+ * converted, holding whatever had not moved yet. Running the convert again is safe: step (a)
38
+ * only refuses a lead that finished converting, and steps (d) and (e) only touch rows still on
39
+ * the lead, so nothing is moved twice - but it makes a second client, and the first one (with
40
+ * whatever had already moved to it) is left for someone to tidy up or delete.
41
+ *
42
+ * Converting needs only `update_leads`: `clients` is the service-role clients resource
43
+ * acting for the caller (`forOrganization(org, user).clientsOnBehalfOfUser()`), so the
44
+ * client is created, and read back, without the caller holding any clients permission.
45
+ */
46
+ convert(leadId: string, input: ConvertLeadDto, clients: ClientsResource): Promise<ClientEntity>;
47
+ /**
48
+ * The two status rules, checked before anything is written, and the lost reason the write
49
+ * stores. A lead reaches its Won status (`is_won`) only by converting, so choosing it is
50
+ * refused (a converted lead is not updated at all). A fail status needs a lost reason; any
51
+ * other status, or none, clears it.
52
+ */
53
+ private lostReasonFor;
54
+ }