Logotipo Hedhog

Get Hedhog updates in your inbox

New releases, fresh recipes, and breaking changes — no spam.

Back to CRM modulesCRM Submodule

CRM Submodule

proposals

Commercial proposal and contract lifecycle — draft, submit for approval, approve/reject/cancel, convert to contract, and generate PDF documents.

Source path: libraries/crm/src/proposal

Module file: proposal.module.ts

Introduction

The Proposals submodule models the full lifecycle of a commercial offer: draft creation, internal approval, client delivery, and conversion into a contract. A proposal moves through a fixed status machine — draft, pending_approval, approved, rejected, cancelled, expired, contract_generated — and every state-changing action is exposed as its own endpoint (submit, send, approve, reject, cancel, convert) rather than a generic status PATCH, which keeps the audit trail and side effects (notifications, approval records) attached to an explicit business action instead of an arbitrary field write.

Internally, every proposal keeps a revision history. PATCH /proposal/:id normally updates the current revision in place, but passing create_new_revision: true forks a new revision (incrementing current_revision_number) instead of overwriting — this lets a sales rep iterate on pricing or scope after a proposal has already been sent without losing the version the client originally saw. Line items (services, products, fees, discounts, notes) and attached documents are children of a revision, not of the proposal itself, so each revision carries its own complete snapshot.

Approval is configurable rather than hardcoded: the number of approvals required before a proposal is considered fully approved is read from the crm-proposal-required-approvals setting (default 1), and each approval is recorded as a row with its own approver_user_id and status so multiple approvers can weigh in independently. Critically, POST /proposal/:id/approve is gated by a dedicated proposal-approver role rather than the general admin-crm role used by the rest of the submodule — this lets an organization designate specific people (e.g. a finance lead or department head) as approval authorities without granting them full CRM administration rights. Submit and send currently route through the same submitForApproval flow on the service, so "sending" a proposal to a client and "submitting" it for internal review are functionally identical today.

POST /proposal/:id/generate-pdf renders the current revision to HTML and then to a PDF buffer, uploads the resulting file through the platform File service (under contact/proposals/generated), and links it back to the proposal as a generated_pdf document — meaning every PDF a proposal ever produced remains retrievable through its document history, not just the latest one. Proposal documents can also originate from a manual upload, an AI-assisted extraction, or an import, distinguished by source_kind, and carry their own extraction_status for tracking OCR/parsing of uploaded source files.

POST /proposal/:id/convert requests conversion of an approved proposal into a contract; this raises an integration event that downstream listeners (such as the checkout/contract listeners wired in CrmModule) can react to, keeping the proposal-to-contract handoff decoupled from the Proposals controller itself.

HTTP Endpoints

13 endpoints

Paginated list of proposals with optional status and person filters. Each item includes required_approvals, approval_count, and whether the current user has already approved it.

Query
page, pageSize, search, status? (draft | pending_approval | approved | rejected | cancelled | expired | contract_generated), person_id?

Summary statistics for proposals (counts per status, total values).

Get full proposal details including items, documents, and approval/workflow history for the current revision.

Params
id (int)

Create a new proposal (defaults to draft status).

Body

FieldTypeRequiredNotes
person_idintyes
titlestring (max 180)yes
codestring (max 40)no
statusProposalStatus (draft | pending_approval | ...)no
contract_categoryProposalContractCategory (employee | contractor | client | supplier | vendor | partner | internal | other)no
contract_typeProposalContractType (clt | pj | freelancer_agreement | service_agreement | fixed_term | recurring_service | nda | amendment | addendum | other)no
billing_modelProposalBillingModel (time_and_material | monthly_retainer | fixed_price)no
currency_codestringno
valid_fromdate stringno
valid_untildate stringno
subtotal_amount_centsintno
discount_amount_centsintno
tax_amount_centsintno
total_amount_centsintno
notesstringno
owner_user_idintno
generation_modeProposalGenerationMode (manual | ai_assisted | duplicated | imported)no
summarystringno
content_htmlstringno
snapshot_jsonobjectno
itemsProposalItemDto[]noLine items (services, products, fees, discounts)
documentsProposalDocumentDto[]noAttached documents

Update proposal fields (partial update; all CreateProposalDto fields accepted).

Params
id (int)

Body

FieldTypeRequiredNotes
create_new_revisionbooleannoFork a new revision instead of overwriting the current one

Render the current revision to PDF, upload it via the File service, and attach it to the proposal as a generated_pdf document.

Params
id (int)

Submit the proposal for internal approval.

Params
id (int)

Body

FieldTypeRequiredNotes
approver_user_idintno
notestringno

Send the proposal to the client (routes through the same submitForApproval flow as /submit).

Params
id (int)

Body

FieldTypeRequiredNotes
approver_user_idintno
notestringno

Approve a proposal that is pending approval. Restricted to the dedicated proposal-approver role, not the general admin-crm role.

Params
id (int)

Body

FieldTypeRequiredNotes
notestringno

Reject a proposal that is pending approval.

Params
id (int)

Body

FieldTypeRequiredNotes
notestringno

Cancel a proposal.

Params
id (int)

Body

FieldTypeRequiredNotes
notestringno

Request conversion of an approved proposal to a contract; raises an integration event for downstream listeners.

Params
id (int)

Body

FieldTypeRequiredNotes
notestringno

Delete one or more proposals.

Body

FieldTypeRequiredNotes
idsnumber[]yesIDs to delete

MCP Tools

Named tools callable by AI models via the Model Context Protocol (MCP).

  • crm.proposals.list

    Lists proposals with pagination and optional filters

  • crm.proposals.stats

    Returns proposal statistics grouped by status

  • crm.proposals.get

    Returns a single proposal by ID with full details including items and documents

  • crm.proposals.create

    Creates a new proposal for a person

  • crm.proposals.update

    Updates an existing proposal by ID

  • crm.proposals.submit

    Submits a proposal for approval

  • crm.proposals.send

    Sends a proposal to the client

  • crm.proposals.approve

    Approves a proposal. Requires proposal-approver role

  • crm.proposals.reject

    Rejects a proposal with an optional reason

  • crm.proposals.cancel

    Cancels a proposal

  • crm.proposals.convert

    Requests conversion of an approved proposal into a contract

  • crm.proposals.delete

    Deletes one or more proposals by their IDs