Skip to content

Commit

Permalink
Merge branch 'develop' into fix/some-permission-logic
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelmbabhazi committed Feb 21, 2025
2 parents d989499 + 5f0db47 commit 8025ad6
Show file tree
Hide file tree
Showing 35 changed files with 1,449 additions and 601 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ import { Subject, firstValueFrom } from 'rxjs';
import { debounceTime, filter, tap } from 'rxjs/operators';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { saveAs } from 'file-saver';
import { IInvoice, IPayment, InvoiceStatusTypesEnum, IOrganization, IUser } from '@gauzy/contracts';
import {
IInvoice,
IPayment,
InvoiceStatusTypesEnum,
IOrganization,
IUser,
IPaymentCreateInput
} from '@gauzy/contracts';
import {
InvoiceEstimateHistoryService,
InvoicesService,
Expand Down Expand Up @@ -118,7 +125,7 @@ export class InvoicePaymentsComponent extends TranslationBaseComponent implement
'toContact',
'payments',
'payments.invoice',
'payments.recordedBy'
'payments.createdByUser'
];

// Fetch invoice details
Expand Down Expand Up @@ -312,12 +319,10 @@ export class InvoicePaymentsComponent extends TranslationBaseComponent implement
instance.value = cell.getValue();
}
},
recordedBy: {
createdByUser: {
title: this.getTranslation('INVOICES_PAGE.PAYMENTS.RECORDED_BY'),
type: 'text',
valuePrepareFunction: (value: IUser) => {
return value && value.name ? `${value.name}` : '';
}
valuePrepareFunction: (value: IUser) => value?.name ?? ''
},
note: {
title: this.getTranslation('INVOICES_PAGE.PAYMENTS.NOTE'),
Expand Down Expand Up @@ -353,7 +358,14 @@ export class InvoicePaymentsComponent extends TranslationBaseComponent implement
};
}

async updateInvoiceStatus(totalValue: number, totalPaid: number) {
/**
* Updates the status of an invoice based on the total invoice value and the total amount paid.
*
* @param totalValue - The total amount due for the invoice.
* @param totalPaid - The total amount that has been paid towards the invoice.
* @returns A Promise that resolves when the invoice status has been updated.
*/
async updateInvoiceStatus(totalValue: number, totalPaid: number): Promise<void> {
if (totalPaid <= 0) {
await this.invoicesService.updateAction(this.invoice.id, {
status: InvoiceStatusTypesEnum.VIEWED
Expand All @@ -377,29 +389,25 @@ export class InvoicePaymentsComponent extends TranslationBaseComponent implement
if (!this.invoice) {
return;
}
// Destructure organization properties
const { tenantId } = this.organization;

const { tenantId } = this.store.user;
const payment = {
// Create a payment object
const payment: IPaymentCreateInput = {
amount: this.leftToPay,
paymentDate: new Date(),
currency: this.invoice.currency,
invoice: this.invoice,
invoiceId: this.invoice.id,
organization: this.invoice.fromOrganization,
organizationId: this.invoice.fromOrganization.id,
tenantId,
recordedBy: this.store.user,
userId: this.store.userId
tenantId
};

if (this.invoice.dueDate >= new Date()) {
payment['overdue'] = true;
} else {
payment['overdue'] = false;
}
payment.overdue = this.invoice.dueDate >= new Date();

await this.paymentService.add(payment);
const { amount, currency, invoice } = payment;

if (payment.invoice) {
const action = this.getTranslation('INVOICES_PAGE.PAYMENTS.PAYMENT_AMOUNT_ADDED', { amount, currency });
await this.createInvoiceHistory(action, invoice);
Expand Down Expand Up @@ -441,7 +449,7 @@ export class InvoicePaymentsComponent extends TranslationBaseComponent implement
contact: this.invoice.toContact ? this.invoice.toContact.name : '',
paymentDate: payment.paymentDate.toString().slice(0, 10),
amount: `${payment.currency + ' ' + payment.amount}`,
recordedBy: payment.recordedBy.firstName + payment.recordedBy.lastName,
createdByUser: payment.createdByUser.name,
note: payment.note || '',
paymentMethod: payment.paymentMethod
? this.getTranslation(`INVOICES_PAGE.PAYMENTS.${payment.paymentMethod}`)
Expand Down
8 changes: 4 additions & 4 deletions apps/gauzy/src/app/pages/payments/payments.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export class PaymentsComponent extends PaginationFilterBaseComponent implements

this.smartTableSource = new ServerDataSource(this.httpClient, {
endPoint: `${API_PREFIX}/payments/pagination`,
relations: ['invoice', 'invoice.toContact', 'recordedBy', 'organizationContact', 'project', 'tags'],
relations: ['invoice', 'invoice.toContact', 'createdByUser', 'organizationContact', 'project', 'tags'],
join: {
alias: 'payment',
leftJoin: {
Expand All @@ -234,15 +234,15 @@ export class PaymentsComponent extends PaginationFilterBaseComponent implements
},
resultMap: (payment: IPayment) => {
try {
const { invoice, project, recordedBy, paymentMethod, overdue } = payment;
const { invoice, project, createdByUser, paymentMethod, overdue } = payment;
const organizationContact = payment.organizationContact || (invoice && invoice.toContact);

return {
...payment,
overdue: this.statusMapper(overdue),
invoiceNumber: invoice?.invoiceNumber || null,
projectName: project?.name || null,
recordedByName: recordedBy?.name || null,
createdByUser: createdByUser?.name || null,
paymentMethodEnum: paymentMethod
? this.getTranslation(`INVOICES_PAGE.PAYMENTS.${paymentMethod}`)
: null,
Expand Down Expand Up @@ -536,7 +536,7 @@ export class PaymentsComponent extends PaginationFilterBaseComponent implements
this.setFilter({ field: 'paymentMethod', search: value });
}
},
recordedByName: {
createdByUser: {
title: this.getTranslation('PAYMENTS_PAGE.RECORDED_BY'),
type: 'text',
isFilterable: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,45 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { catchError, map, Observable, of, switchMap, tap } from 'rxjs';
import { filter } from 'rxjs/operators';
import { catchError, filter, map, Observable, of, switchMap, tap } from 'rxjs';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { TranslateService } from '@ngx-translate/core';
import { IPipeline, IContact, IOrganization, IDeal, IPagination } from '@gauzy/contracts';
import { IPipeline, IOrganization, IDeal, IPagination, IOrganizationContact } from '@gauzy/contracts';
import { distinctUntilChange } from '@gauzy/ui-core/common';
import { TranslationBaseComponent } from '@gauzy/ui-core/i18n';
import {
DealsService,
ErrorHandlingService,
OrganizationContactService,
Store,
ToastrService
} from '@gauzy/ui-core/core';
import { TranslationBaseComponent } from '@gauzy/ui-core/i18n';

@UntilDestroy({ checkProperties: true })
@Component({
templateUrl: './pipeline-deal-form.component.html',
selector: 'ga-pipeline-deals-form',
templateUrl: './pipeline-deal-form.component.html',
styleUrls: ['./pipeline-deal-form.component.scss']
})
export class PipelineDealFormComponent extends TranslationBaseComponent implements OnInit, OnDestroy {
public selectedClient: IContact;
public probabilities = [0, 1, 2, 3, 4, 5];
public selectedProbability: number;
public organization: IOrganization;
public deal: IDeal;
public deal$: Observable<IDeal>;
public pipeline: IPipeline;
public pipeline$: Observable<IPipeline>;
public clients: IContact[] = [];
public clients$: Observable<IContact[]>;

// Form Builder
public form: UntypedFormGroup = PipelineDealFormComponent.buildForm(this._fb);
static buildForm(fb: UntypedFormBuilder): UntypedFormGroup {
return fb.group({
stageId: [null, Validators.required],
title: [null, Validators.required],
clientId: [null],
probability: [null, Validators.required]
});
}
public selectedClient: IOrganizationContact;
public clients: IOrganizationContact[] = [];
public clients$: Observable<IOrganizationContact[]>;

// Form Builders
public form: UntypedFormGroup = this._fb.group({
stageId: [null, Validators.required],
title: [null, Validators.required],
clientId: [null],
probability: [null, Validators.required]
});

constructor(
public readonly translateService: TranslateService,
Expand Down Expand Up @@ -79,7 +75,7 @@ export class PipelineDealFormComponent extends TranslationBaseComponent implemen
});
}),
// Map the contacts to the clients property
map(({ items }: IPagination<IContact>) => items),
map(({ items }: IPagination<IOrganizationContact>) => items),
// Handle errors
catchError((error) => {
console.error('Error fetching organization contacts:', error);
Expand Down Expand Up @@ -132,11 +128,10 @@ export class PipelineDealFormComponent extends TranslationBaseComponent implemen
* @param deal The deal object containing data to patch into the form
*/
patchFormValue(deal: IDeal) {
const { title, stageId, createdBy, probability, clientId } = deal;
const { title, stageId, probability, clientId } = deal;
this.form.patchValue({
title,
stageId,
createdBy,
probability,
clientId
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ import { IDeal } from '@gauzy/contracts';

@Component({
selector: 'ga-pipeline-deal-created-by',
template: `{{ rowData?.createdBy?.name }}`
template: `{{ rowData?.createdByUser?.name }}`
})
export class PipelineDealCreatedByComponent {
@Input()
value: string | number;

@Input()
rowData: IDeal;
@Input() value: string | number;
@Input() rowData: IDeal;
}
27 changes: 13 additions & 14 deletions packages/contracts/src/lib/deal.model.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import { IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model';
import { IUser } from './user.model';
import { IHasUserCreator } from './user.model';
import { IPipelineStage } from './pipeline-stage.model';
import { IContact } from './contact.model';
import { IOrganizationContact } from './organization-contact.model';

export interface IDeal extends IBasePerTenantAndOrganizationEntityModel {
// Common base interface for deal fields
interface IDealBase extends IBasePerTenantAndOrganizationEntityModel {
title: string;
probability?: number;
createdBy: IUser;
createdByUserId: ID;
probability: number;
stage: IPipelineStage;
stageId: ID;
client?: IContact;
client?: IOrganizationContact;
clientId?: ID;
}

export type IDealFindInput = Partial<IDealCreateInput>;
// IDeal interface with the additional stage and user creator properties
export interface IDeal extends IDealBase, IHasUserCreator {}

export interface IDealCreateInput extends IBasePerTenantAndOrganizationEntityModel {
stageId: ID;
clientId?: ID;
title: string;
probability?: number;
}
// IDealCreateInput interface, omitting user-related fields from IDeal
export interface IDealCreateInput extends IDealBase {}

// IDealFindInput type, based on IDealCreateInput
export type IDealFindInput = Partial<IDealBase>;
40 changes: 17 additions & 23 deletions packages/contracts/src/lib/organization-team.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,24 @@ import { CrudActionEnum } from './organization.model';
import { IOrganizationProject, IOrganizationProjectCreateInput } from './organization-projects.model';
import { IOrganizationProjectModule } from './organization-project-module.model';
import { IComment } from './comment.model';
import { IHasUserCreator } from './user.model';

export interface IOrganizationTeam extends IBasePerTenantAndOrganizationEntityModel, IRelationalImageAsset, ITaggable {
// Base interface for common properties
interface IBaseTeamProperties extends IBasePerTenantAndOrganizationEntityModel, IRelationalImageAsset, ITaggable {
name: string;
color?: string;
emoji?: string;
teamSize?: string;
logo?: string;
prefix?: string;
shareProfileView?: boolean; // If true, all members can view "Worked" tasks and "Daily Plan" tabs of all other employees, By default, it's true
requirePlanToTrack?: boolean; // If true, members can't be able to track time without have a "Daily Plan". By default, it's false
shareProfileView?: boolean; // Default is true
requirePlanToTrack?: boolean; // Default is false
public?: boolean;
profile_link?: string;
}

// Interface for team members and related entities
interface ITeamAssociations {
members?: IOrganizationTeamEmployee[];
managers?: IOrganizationTeamEmployee[];
projects?: IOrganizationProject[];
Expand All @@ -29,6 +35,10 @@ export interface IOrganizationTeam extends IBasePerTenantAndOrganizationEntityMo
tasks?: ITask[];
}

// Main Organization Team interface
export interface IOrganizationTeam extends IBaseTeamProperties, ITeamAssociations, IHasUserCreator {}

// Input interface for finding an organization team
export interface IOrganizationTeamFindInput extends IBasePerTenantAndOrganizationEntityModel, IEmployeeEntityInput {
name?: string;
prefix?: string;
Expand All @@ -37,29 +47,13 @@ export interface IOrganizationTeamFindInput extends IBasePerTenantAndOrganizatio
members?: IOrganizationTeamEmployee;
}

export interface IOrganizationTeamCreateInput
extends IBasePerTenantAndOrganizationEntityModel,
IRelationalImageAsset,
IMemberEntityBased,
ITaggable {
name: string;
emoji?: string;
teamSize?: string;
color?: string;
logo?: string;
prefix?: string;
shareProfileView?: boolean;
requirePlanToTrack?: boolean;
public?: boolean;
profile_link?: string;
// Input interface for creating an organization team
export interface IOrganizationTeamCreateInput extends IBaseTeamProperties, IMemberEntityBased {
projects?: IOrganizationProjectCreateInput[];
}

export interface IOrganizationTeamUpdateInput extends Partial<IOrganizationTeamCreateInput> {
shareProfileView?: boolean;
requirePlanToTrack?: boolean;
public?: boolean;
}
// Input interface for updating an organization team
export interface IOrganizationTeamUpdateInput extends Partial<IOrganizationTeamCreateInput> {}

export interface IOrganizationTeamStatisticInput extends ITimerStatusInput {
withLastWorkedTask: boolean;
Expand Down
Loading

0 comments on commit 8025ad6

Please sign in to comment.