Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ template (the next template still runs).
| c | render | the template is copied to a temp dir (without `template.toml`), every `{{token}}` is replaced with its `default` (or a `Sample …` stand-in), and a `project.toml` is written (`[document]` + `[build] entry = "main.tex"`, plus `bibliography = "bib/references.bib"` when that file exists) |
| d | texforge | in the rendered copy: `texforge check` exits 0 **and** prints no line matching `WARNING\|ERROR`; `texforge fmt --check` exits 0; `texforge build` exits 0; `texforge pdf check` exits 0 |
| e | previews | `texforge preview --scale 1.5` writes PNG pages into `<git-dir>/template-previews/<name>/` (the directory is emptied first), and the page count is read from `texforge pdf info` |
| f | content | *(requirements skipped for `letter`, `cv` and `contract`)* `main.tex` or a file it `\input`s contains a `\begin{code}[lang=…]` block, and at least one `\begin{mermaid}` / `\begin{graphviz}` / `\begin{d2}` whose options contain `style=`. For **every** template: no `\usepackage[utf8]{inputenc}`, no `\lstdefinestyle`, no `\begin{lstlisting}`, no `\usepackage{minted}` |
| f | content | *(requirements skipped for `letter`, `cv`, `contract` and `quotation`)* `main.tex` or a file it `\input`s contains a `\begin{code}[lang=…]` block, and at least one `\begin{mermaid}` / `\begin{graphviz}` / `\begin{d2}` whose options contain `style=`. For **every** template: no `\usepackage[utf8]{inputenc}`, no `\lstdefinestyle`, no `\begin{lstlisting}`, no `\usepackage{minted}` |

### Where previews land

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ All templates support **multi-language output** via the `{{language}}` placehold
| `ieee` | IEEE journal format for technical papers | 0.2.1 |
| `internal-docs` | Handbook for internal policies, procedures, playbooks and decision records, with a cover, document control and callouts | 0.1.0 |
| `letter` | Formal letter template for professional correspondence | 0.3.0 |
| `quotation` | Service quotation with itemised prices, scope, 50/50 payment terms and acceptance through the signed service order | 0.1.0 |
| `report` | Structured university report with front matter, contents and appendices | 0.4.0 |
| `slides` | 16:9 presentation slides with code and diagrams | 0.2.0 |
| `technical-brief` | Concise technical update with callouts, diagrams, code and results | 0.3.0 |
Expand Down
4 changes: 2 additions & 2 deletions check-templates.sh
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ for p, text in files:
if pat in text:
print("forbidden pattern " + pat + " in " + str(p.relative_to(dest)))
raise SystemExit(1)
if name in ("letter", "cv", "contract"):
raise SystemExit(0) # content requirements are skipped for letter/cv/contract
if name in ("letter", "cv", "contract", "quotation"):
raise SystemExit(0) # content requirements are skipped for letter/cv/contract/quotation
entry = dest / "main.tex"
if not entry.is_file():
print("main.tex not found"); raise SystemExit(1)
Expand Down
237 changes: 237 additions & 0 deletions quotation/main.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
% Service quotation sent to a client before a service order. It is not a
% contract: the client accepts it by signing the service order electronically.
% Text in double braces is filled in when the project is created.
\documentclass[11pt,a4paper]{article}

\usepackage[{{language}}]{babel}
\usepackage[T1]{fontenc}
\usepackage[lining]{ebgaramond}
\usepackage[scale=0.92]{sourcesanspro}
\usepackage{microtype}
\usepackage[a4paper,left=28mm,right=28mm,top=26mm,bottom=30mm,
headheight=14pt,headsep=10mm,footskip=12mm]{geometry}
\usepackage{xcolor}
\usepackage{enumitem}
\usepackage{fancyhdr}
\usepackage{booktabs}
\usepackage{tabularx}
\usepackage{lastpage}
\usepackage{needspace}
\usepackage{xfp}
\usepackage{siunitx}
\usepackage{etoolbox}
\usepackage{hyperref}

% Palette: garnet accent (placeholder, garnet by default) on ink and sepia
% greys. No gold, no teal, plain paper.
\definecolor{conink}{HTML}{1D1510}
\definecolor{conmuted}{HTML}{6B5A43}
\colorlet{conrule}{conmuted!35!white}
\definecolor{conaccent}{HTML}{{{accent}}}
\hypersetup{colorlinks=true,linkcolor=conaccent,urlcolor=conaccent,
pdftitle={Quotation}}

% Numbers: English by default; Spanish overrides the separators below.
\sisetup{group-separator={,},output-decimal-marker={.},group-minimum-digits=4}

% Fixed labels and phrases. English defaults, then Spanish overrides.
\newcommand{\lblTitle}{Quotation}
\newcommand{\lblQuoteNo}{Quote no.}
\newcommand{\lblIssued}{Issue date}
\newcommand{\lblValidity}{Validity (days)}
\newcommand{\lblCurrency}{Currency}
\newcommand{\lblIssuer}{Issuer}
\newcommand{\lblClient}{Client}
\newcommand{\lblName}{Name}
\newcommand{\lblId}{ID or tax number}
\newcommand{\lblEmail}{Email}
\newcommand{\lblSecNeed}{Need}
\newcommand{\lblSecScope}{Scope}
\newcommand{\lblIn}{Included}
\newcommand{\lblOut}{Not included}
\newcommand{\lblSecItems}{Itemised prices}
\newcommand{\lblItem}{Item}
\newcommand{\lblDesc}{Description}
\newcommand{\lblQty}{Qty}
\newcommand{\lblUnit}{Unit price}
\newcommand{\lblLineTotal}{Total}
\newcommand{\lblSubtotal}{Subtotal}
\newcommand{\lblTotalAmt}{Total}
\newcommand{\lblSecPay}{Payment terms}
\newcommand{\lblWhen}{When}
\newcommand{\lblPct}{Share}
\newcommand{\lblAmount}{Amount}
\newcommand{\lblAtStart}{At signing of the service order}
\newcommand{\lblOnDelivery}{On delivery}
\newcommand{\lblMethods}{Payment methods}
\newcommand{\feesnote}{Payment gateway and bank transfer fees (for example PayPal) are paid by the client.}
\newcommand{\lblSecTax}{Tax}
\newcommand{\lblSecTime}{Estimated timeline}
\newcommand{\lblSecAccept}{How to accept}
\newcommand{\acceptext}[1]{This quotation is accepted by signing the service order electronically at #1. This quotation alone is not a contract.}
\newcommand{\lblQuoteNoFoot}{Quotation}
\addto\captionsspanish{%
\renewcommand{\lblTitle}{Cotización}
\renewcommand{\lblQuoteNo}{Cotización n.º}
\renewcommand{\lblIssued}{Fecha de emisión}
\renewcommand{\lblValidity}{Vigencia (días)}
\renewcommand{\lblCurrency}{Moneda}
\renewcommand{\lblIssuer}{Emisor}
\renewcommand{\lblClient}{Cliente}
\renewcommand{\lblName}{Nombre}
\renewcommand{\lblId}{Identificación o NIT}
\renewcommand{\lblEmail}{Correo electrónico}
\renewcommand{\lblSecNeed}{Necesidad}
\renewcommand{\lblSecScope}{Alcance}
\renewcommand{\lblIn}{Incluye}
\renewcommand{\lblOut}{No incluye}
\renewcommand{\lblSecItems}{Detalle económico}
\renewcommand{\lblItem}{Ítem}
\renewcommand{\lblDesc}{Descripción}
\renewcommand{\lblQty}{Cant.}
\renewcommand{\lblUnit}{Precio unitario}
\renewcommand{\lblLineTotal}{Total}
\renewcommand{\lblSubtotal}{Subtotal}
\renewcommand{\lblTotalAmt}{Total}
\renewcommand{\lblSecPay}{Condiciones de pago}
\renewcommand{\lblWhen}{Momento}
\renewcommand{\lblPct}{Porcentaje}
\renewcommand{\lblAmount}{Valor}
\renewcommand{\lblAtStart}{Al firmar la orden de servicio}
\renewcommand{\lblOnDelivery}{Contra entrega}
\renewcommand{\lblMethods}{Medios de pago}
\renewcommand{\feesnote}{Los cargos de la pasarela y de las transferencias (por ejemplo, PayPal) corren por cuenta del cliente.}
\renewcommand{\lblSecTax}{Impuestos}
\renewcommand{\lblSecTime}{Cronograma estimado}
\renewcommand{\lblSecAccept}{Cómo aceptar}
\renewcommand{\acceptext}[1]{Esta cotización se acepta firmando electrónicamente la orden de servicio en #1. La cotización, por sí sola, no constituye un contrato.}
\renewcommand{\lblQuoteNoFoot}{Cotización}
% Spanish groups thousands with a full stop; a bare full stop is dropped by siunitx, so it is braced.
\sisetup{group-separator={{.}},output-decimal-marker={,}}}

% Values from the placeholders, read once by the totals below.
\newcommand{\QtyA}{{{item_1_qty}}}
\newcommand{\PriceA}{{{item_1_price}}}
\newcommand{\QtyB}{{{item_2_qty}}}
\newcommand{\PriceB}{{{item_2_price}}}
\newcommand{\QtyC}{{{item_3_qty}}}
\newcommand{\PriceC}{{{item_3_price}}}
\newcommand{\AdvPct}{{{advance_pct}}}
\newcommand{\QuoteNo}{{{quote_number}}}

% Small helpers: section headings, metadata labels and the party blocks.
\newcommand{\metalabel}[1]{{\sffamily\scriptsize\scshape\color{conmuted}#1}}
\newcommand{\sechead}[1]{\par\vspace{1.2em}\needspace{6\baselineskip}%
{\sffamily\small\bfseries\scshape\color{conaccent}#1}\par
\vspace{2pt}{\color{conrule}\hrule height 0.4pt}\par\vspace{6pt}}
\newcommand{\subhead}[1]{\par\vspace{4pt}{\sffamily\small\bfseries\color{conink}#1}\par}
\newcommand{\party}[4]{\begin{minipage}[t]{0.47\textwidth}
{\sffamily\small\scshape\bfseries\color{conaccent}#1}\par\vspace{2pt}
{\color{conaccent}\hrule height 0.8pt}\par\vspace{6pt}
{\bfseries #2}\par\vspace{5pt}
\metalabel{\lblId}\par #3\par\vspace{5pt}
\metalabel{\lblEmail}\par #4
\end{minipage}}

% Page furniture: quote number and page count on every page.
\setlength{\parindent}{0pt}
\setlength{\parskip}{0.5\baselineskip}
\setlength{\emergencystretch}{2em}
\setlist[itemize]{leftmargin=1.2em,itemsep=2pt,topsep=2pt,parsep=0pt}
\renewcommand{\arraystretch}{1.3}
\fancypagestyle{quote}{%
\fancyhf{}
\renewcommand{\headrulewidth}{0pt}
\fancyfoot[L]{\small\sffamily {{lab_name}}\quad\textperiodcentered\quad\lblQuoteNoFoot\ \QuoteNo}
\fancyfoot[R]{\small\sffamily\thepage\ /\ \pageref*{LastPage}}
\renewcommand{\footrulewidth}{0pt}}
\pagestyle{quote}

\begin{document}

% Totals: each line is quantity times unit price; subtotal, advance and balance.
\edef\TotA{\fpeval{\QtyA*\PriceA}}
\edef\TotB{\fpeval{\QtyB*\PriceB}}
\edef\TotC{\fpeval{\QtyC*\PriceC}}
\edef\Sub{\fpeval{\TotA+\TotB+\TotC}}
\edef\Adv{\fpeval{\Sub*\AdvPct/100}}
\edef\Bal{\fpeval{\Sub-\Adv}}
\edef\BalPct{\fpeval{100-\AdvPct}}

% Header: quote number, title, the issuer and the client.
{\sffamily\small\scshape\color{conaccent}\lblQuoteNo\ \QuoteNo\par}
\vspace{4pt}
{\normalfont\fontsize{30}{34}\selectfont\color{conink}\lblTitle\par}
\vspace{6pt}
{\color{conaccent}\rule{22mm}{2pt}\par}
\vspace{10pt}

\noindent
\party{\lblIssuer}{{{lab_name}}\\{{owner_name}}}{{{owner_id}}}{{{owner_email}}\\{{lab_site}}}%
\hfill
\party{\lblClient}{{{client_name}}}{{{client_id}}}{{{client_contact}}}

\vspace{12pt}
\noindent\begin{tabularx}{\textwidth}{@{}X X X@{}}
\metalabel{\lblIssued} & \metalabel{\lblValidity} & \metalabel{\lblCurrency}\\[2pt]
{{issue_date}} & {{validity_days}} & {{currency}}
\end{tabularx}

\sechead{\lblSecNeed}
{{service_need}}

\sechead{\lblSecScope}
\subhead{\lblIn}
\begin{itemize}
\item {{scope_in_1}}
\item {{scope_in_2}}
\item {{scope_in_3}}
\end{itemize}
\subhead{\lblOut}
\begin{itemize}
\item {{scope_out_1}}
\item {{scope_out_2}}
\end{itemize}

\sechead{\lblSecItems}
\begin{tabularx}{\textwidth}{@{}l X r r r@{}}
\toprule
\textbf{\lblItem} & \textbf{\lblDesc} & \textbf{\lblQty} & \textbf{\lblUnit} & \textbf{\lblLineTotal}\\
\midrule
{{item_1_name}} & {{item_1_desc}} & \num{\QtyA} & \num{\PriceA} & \num{\TotA}\\
{{item_2_name}} & {{item_2_desc}} & \num{\QtyB} & \num{\PriceB} & \num{\TotB}\\
{{item_3_name}} & {{item_3_desc}} & \num{\QtyC} & \num{\PriceC} & \num{\TotC}\\
\midrule
\multicolumn{4}{r}{\lblSubtotal} & \num{\Sub}\\
\multicolumn{4}{r}{\textbf{\lblTotalAmt}\ ({{currency}})} & \textbf{\num{\Sub}}\\
\bottomrule
\end{tabularx}

\sechead{\lblSecPay}
\begin{tabularx}{\textwidth}{@{}X r r@{}}
\toprule
\textbf{\lblWhen} & \textbf{\lblPct} & \textbf{\lblAmount}\\
\midrule
\lblAtStart & \AdvPct\,\% & \num{\Adv}\\
\lblOnDelivery & \BalPct\,\% & \num{\Bal}\\
\bottomrule
\end{tabularx}
\par\vspace{6pt}
{\itshape\small\color{conmuted}\feesnote\par}
\vspace{4pt}
\metalabel{\lblMethods}\par {{payment_methods}}

\sechead{\lblSecTax}
{{tax_note}}

\sechead{\lblSecTime}
{{timeline}}

\sechead{\lblSecAccept}
\acceptext{{{lab_site}}}

\needspace{4\baselineskip}
\vspace{1.6em}
{\itshape\color{conmuted}{{closing}}\par}

\end{document}
7 changes: 7 additions & 0 deletions quotation/spell-whitelist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Words the spell checker accepts in this template. One word per line.
pt
cop
usd
iva
paypal
hosting
Loading
Loading