Progress on the go-rewrite

This commit is contained in:
Karl Cordes 2025-07-07 07:34:12 +10:00
parent 4f54a93c62
commit 28b331737f
12 changed files with 509 additions and 749 deletions

View file

@ -167,6 +167,9 @@ func main() {
w.Write([]byte(`{"status":"ok"}`))
}).Methods("GET")
// Recent activity endpoint
r.HandleFunc("/api/recent-activity", documentHandler.GetRecentActivity).Methods("GET")
// Page routes
r.HandleFunc("/", pageHandler.Home).Methods("GET")

View file

@ -11,39 +11,40 @@ import (
"time"
)
const archiveDocument = `-- name: ArchiveDocument :exec
DELETE FROM documents
WHERE id = ?
const countDocuments = `-- name: CountDocuments :one
SELECT COUNT(*) FROM documents
`
func (q *Queries) ArchiveDocument(ctx context.Context, id int32) error {
_, err := q.db.ExecContext(ctx, archiveDocument, id)
return err
func (q *Queries) CountDocuments(ctx context.Context) (int64, error) {
row := q.db.QueryRowContext(ctx, countDocuments)
var count int64
err := row.Scan(&count)
return count, err
}
const countDocumentsByType = `-- name: CountDocumentsByType :one
SELECT COUNT(*) FROM documents WHERE type = ?
`
func (q *Queries) CountDocumentsByType(ctx context.Context, type_ DocumentsType) (int64, error) {
row := q.db.QueryRowContext(ctx, countDocumentsByType, type_)
var count int64
err := row.Scan(&count)
return count, err
}
const createDocument = `-- name: CreateDocument :execresult
INSERT INTO documents (
type,
created,
user_id,
doc_page_count,
cmc_reference,
pdf_filename,
pdf_created_at,
pdf_created_by_user_id,
shipping_details,
revision,
bill_to,
ship_to,
email_sent_at,
email_sent_by_user_id
) VALUES (
?, NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
type, created, user_id, doc_page_count, cmc_reference,
pdf_filename, pdf_created_at, pdf_created_by_user_id,
shipping_details, revision, bill_to, ship_to,
email_sent_at, email_sent_by_user_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`
type CreateDocumentParams struct {
Type DocumentsType `json:"type"`
Created time.Time `json:"created"`
UserID int32 `json:"user_id"`
DocPageCount int32 `json:"doc_page_count"`
CmcReference string `json:"cmc_reference"`
@ -61,6 +62,7 @@ type CreateDocumentParams struct {
func (q *Queries) CreateDocument(ctx context.Context, arg CreateDocumentParams) (sql.Result, error) {
return q.db.ExecContext(ctx, createDocument,
arg.Type,
arg.Created,
arg.UserID,
arg.DocPageCount,
arg.CmcReference,
@ -76,219 +78,230 @@ func (q *Queries) CreateDocument(ctx context.Context, arg CreateDocumentParams)
)
}
const getDocumentByID = `-- name: GetDocumentByID :one
SELECT
d.id,
d.type,
d.created,
d.user_id,
d.doc_page_count,
d.pdf_filename,
d.pdf_created_at,
d.pdf_created_by_user_id,
d.cmc_reference,
d.revision,
d.shipping_details,
d.bill_to,
d.ship_to,
d.email_sent_at,
d.email_sent_by_user_id,
u.first_name as user_first_name,
u.last_name as user_last_name,
u.username as user_username,
pdf_creator.first_name as pdf_creator_first_name,
pdf_creator.last_name as pdf_creator_last_name,
pdf_creator.username as pdf_creator_username,
COALESCE(ec.name, ic.name, '') as customer_name,
e.title as enquiry_title
FROM documents d
LEFT JOIN users u ON d.user_id = u.id
LEFT JOIN users pdf_creator ON d.pdf_created_by_user_id = pdf_creator.id
LEFT JOIN enquiries e ON d.type IN ('quote', 'orderAck') AND d.cmc_reference = e.title
LEFT JOIN customers ec ON e.customer_id = ec.id
LEFT JOIN invoices i ON d.type = 'invoice' AND d.cmc_reference = i.title
LEFT JOIN customers ic ON i.customer_id = ic.id
WHERE d.id = ?
const deleteDocument = `-- name: DeleteDocument :exec
DELETE FROM documents WHERE id = ?
`
type GetDocumentByIDRow struct {
ID int32 `json:"id"`
Type DocumentsType `json:"type"`
Created time.Time `json:"created"`
UserID int32 `json:"user_id"`
DocPageCount int32 `json:"doc_page_count"`
PdfFilename string `json:"pdf_filename"`
PdfCreatedAt time.Time `json:"pdf_created_at"`
PdfCreatedByUserID int32 `json:"pdf_created_by_user_id"`
CmcReference string `json:"cmc_reference"`
Revision int32 `json:"revision"`
ShippingDetails sql.NullString `json:"shipping_details"`
BillTo sql.NullString `json:"bill_to"`
ShipTo sql.NullString `json:"ship_to"`
EmailSentAt time.Time `json:"email_sent_at"`
EmailSentByUserID int32 `json:"email_sent_by_user_id"`
UserFirstName sql.NullString `json:"user_first_name"`
UserLastName sql.NullString `json:"user_last_name"`
UserUsername sql.NullString `json:"user_username"`
PdfCreatorFirstName sql.NullString `json:"pdf_creator_first_name"`
PdfCreatorLastName sql.NullString `json:"pdf_creator_last_name"`
PdfCreatorUsername sql.NullString `json:"pdf_creator_username"`
CustomerName string `json:"customer_name"`
EnquiryTitle sql.NullString `json:"enquiry_title"`
func (q *Queries) DeleteDocument(ctx context.Context, id int32) error {
_, err := q.db.ExecContext(ctx, deleteDocument, id)
return err
}
func (q *Queries) GetDocumentByID(ctx context.Context, id int32) (GetDocumentByIDRow, error) {
row := q.db.QueryRowContext(ctx, getDocumentByID, id)
var i GetDocumentByIDRow
const getDocument = `-- name: GetDocument :one
SELECT id, type, created, user_id, doc_page_count, cmc_reference, pdf_filename, pdf_created_at, pdf_created_by_user_id, shipping_details, revision, bill_to, ship_to, email_sent_at, email_sent_by_user_id FROM documents WHERE id = ?
`
func (q *Queries) GetDocument(ctx context.Context, id int32) (Document, error) {
row := q.db.QueryRowContext(ctx, getDocument, id)
var i Document
err := row.Scan(
&i.ID,
&i.Type,
&i.Created,
&i.UserID,
&i.DocPageCount,
&i.CmcReference,
&i.PdfFilename,
&i.PdfCreatedAt,
&i.PdfCreatedByUserID,
&i.CmcReference,
&i.Revision,
&i.ShippingDetails,
&i.Revision,
&i.BillTo,
&i.ShipTo,
&i.EmailSentAt,
&i.EmailSentByUserID,
&i.UserFirstName,
&i.UserLastName,
&i.UserUsername,
&i.PdfCreatorFirstName,
&i.PdfCreatorLastName,
&i.PdfCreatorUsername,
&i.CustomerName,
&i.EnquiryTitle,
)
return i, err
}
const getPurchaseOrderByDocumentID = `-- name: GetPurchaseOrderByDocumentID :one
SELECT
po.id,
po.title,
po.principle_id,
po.principle_reference,
po.issue_date,
po.ordered_from,
po.dispatch_by,
po.deliver_to,
po.shipping_instructions
FROM purchase_orders po
JOIN documents d ON d.cmc_reference = po.title
WHERE d.id = ? AND d.type = 'purchaseOrder'
`
type GetPurchaseOrderByDocumentIDRow struct {
ID int32 `json:"id"`
Title string `json:"title"`
PrincipleID int32 `json:"principle_id"`
PrincipleReference string `json:"principle_reference"`
IssueDate time.Time `json:"issue_date"`
OrderedFrom string `json:"ordered_from"`
DispatchBy string `json:"dispatch_by"`
DeliverTo string `json:"deliver_to"`
ShippingInstructions string `json:"shipping_instructions"`
}
func (q *Queries) GetPurchaseOrderByDocumentID(ctx context.Context, id int32) (GetPurchaseOrderByDocumentIDRow, error) {
row := q.db.QueryRowContext(ctx, getPurchaseOrderByDocumentID, id)
var i GetPurchaseOrderByDocumentIDRow
err := row.Scan(
&i.ID,
&i.Title,
&i.PrincipleID,
&i.PrincipleReference,
&i.IssueDate,
&i.OrderedFrom,
&i.DispatchBy,
&i.DeliverTo,
&i.ShippingInstructions,
)
return i, err
}
const listDocuments = `-- name: ListDocuments :many
const getDocumentWithUser = `-- name: GetDocumentWithUser :one
SELECT
d.id,
d.type,
d.created,
d.user_id,
d.doc_page_count,
d.cmc_reference,
d.pdf_filename,
d.pdf_created_at,
d.pdf_created_by_user_id,
d.cmc_reference,
d.shipping_details,
d.revision,
d.bill_to,
d.ship_to,
d.email_sent_at,
d.email_sent_by_user_id,
u.username as user_username,
u.first_name as user_first_name,
u.last_name as user_last_name,
u.username as user_username,
pdf_creator.first_name as pdf_creator_first_name,
pdf_creator.last_name as pdf_creator_last_name,
pdf_creator.username as pdf_creator_username
u.email as user_email,
pu.username as pdf_creator_username,
pu.first_name as pdf_creator_first_name,
pu.last_name as pdf_creator_last_name,
pu.email as pdf_creator_email
FROM documents d
LEFT JOIN users u ON d.user_id = u.id
LEFT JOIN users pdf_creator ON d.pdf_created_by_user_id = pdf_creator.id
ORDER BY d.id DESC
LIMIT 1000
LEFT JOIN users pu ON d.pdf_created_by_user_id = pu.id
WHERE d.id = ?
`
type ListDocumentsRow struct {
type GetDocumentWithUserRow struct {
ID int32 `json:"id"`
Type DocumentsType `json:"type"`
Created time.Time `json:"created"`
UserID int32 `json:"user_id"`
DocPageCount int32 `json:"doc_page_count"`
CmcReference string `json:"cmc_reference"`
PdfFilename string `json:"pdf_filename"`
PdfCreatedAt time.Time `json:"pdf_created_at"`
PdfCreatedByUserID int32 `json:"pdf_created_by_user_id"`
CmcReference string `json:"cmc_reference"`
ShippingDetails sql.NullString `json:"shipping_details"`
Revision int32 `json:"revision"`
BillTo sql.NullString `json:"bill_to"`
ShipTo sql.NullString `json:"ship_to"`
EmailSentAt time.Time `json:"email_sent_at"`
EmailSentByUserID int32 `json:"email_sent_by_user_id"`
UserUsername sql.NullString `json:"user_username"`
UserFirstName sql.NullString `json:"user_first_name"`
UserLastName sql.NullString `json:"user_last_name"`
UserUsername sql.NullString `json:"user_username"`
UserEmail sql.NullString `json:"user_email"`
PdfCreatorUsername sql.NullString `json:"pdf_creator_username"`
PdfCreatorFirstName sql.NullString `json:"pdf_creator_first_name"`
PdfCreatorLastName sql.NullString `json:"pdf_creator_last_name"`
PdfCreatorUsername sql.NullString `json:"pdf_creator_username"`
PdfCreatorEmail sql.NullString `json:"pdf_creator_email"`
}
func (q *Queries) ListDocuments(ctx context.Context) ([]ListDocumentsRow, error) {
rows, err := q.db.QueryContext(ctx, listDocuments)
func (q *Queries) GetDocumentWithUser(ctx context.Context, id int32) (GetDocumentWithUserRow, error) {
row := q.db.QueryRowContext(ctx, getDocumentWithUser, id)
var i GetDocumentWithUserRow
err := row.Scan(
&i.ID,
&i.Type,
&i.Created,
&i.UserID,
&i.DocPageCount,
&i.CmcReference,
&i.PdfFilename,
&i.PdfCreatedAt,
&i.PdfCreatedByUserID,
&i.ShippingDetails,
&i.Revision,
&i.BillTo,
&i.ShipTo,
&i.EmailSentAt,
&i.EmailSentByUserID,
&i.UserUsername,
&i.UserFirstName,
&i.UserLastName,
&i.UserEmail,
&i.PdfCreatorUsername,
&i.PdfCreatorFirstName,
&i.PdfCreatorLastName,
&i.PdfCreatorEmail,
)
return i, err
}
const getRecentDocuments = `-- name: GetRecentDocuments :many
SELECT
d.id,
d.type,
d.created,
d.cmc_reference,
d.pdf_filename,
d.revision,
u.username as created_by_username,
CASE
WHEN d.type = 'quote' THEN CONCAT('Quote ', d.cmc_reference)
WHEN d.type = 'invoice' THEN CONCAT('Invoice ', d.cmc_reference)
WHEN d.type = 'purchaseOrder' THEN CONCAT('Purchase Order ', d.cmc_reference)
WHEN d.type = 'orderAck' THEN CONCAT('Order Ack ', d.cmc_reference)
WHEN d.type = 'packingList' THEN CONCAT('Packing List ', d.cmc_reference)
ELSE CONCAT(d.type, ' ', d.cmc_reference)
END as display_name
FROM documents d
LEFT JOIN users u ON d.user_id = u.id
ORDER BY d.created DESC
LIMIT ?
`
type GetRecentDocumentsRow struct {
ID int32 `json:"id"`
Type DocumentsType `json:"type"`
Created time.Time `json:"created"`
CmcReference string `json:"cmc_reference"`
PdfFilename string `json:"pdf_filename"`
Revision int32 `json:"revision"`
CreatedByUsername sql.NullString `json:"created_by_username"`
DisplayName interface{} `json:"display_name"`
}
func (q *Queries) GetRecentDocuments(ctx context.Context, limit int32) ([]GetRecentDocumentsRow, error) {
rows, err := q.db.QueryContext(ctx, getRecentDocuments, limit)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListDocumentsRow{}
items := []GetRecentDocumentsRow{}
for rows.Next() {
var i ListDocumentsRow
var i GetRecentDocumentsRow
if err := rows.Scan(
&i.ID,
&i.Type,
&i.Created,
&i.CmcReference,
&i.PdfFilename,
&i.Revision,
&i.CreatedByUsername,
&i.DisplayName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listDocuments = `-- name: ListDocuments :many
SELECT id, type, created, user_id, doc_page_count, cmc_reference, pdf_filename, pdf_created_at, pdf_created_by_user_id, shipping_details, revision, bill_to, ship_to, email_sent_at, email_sent_by_user_id FROM documents ORDER BY created DESC LIMIT ? OFFSET ?
`
type ListDocumentsParams struct {
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
func (q *Queries) ListDocuments(ctx context.Context, arg ListDocumentsParams) ([]Document, error) {
rows, err := q.db.QueryContext(ctx, listDocuments, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
items := []Document{}
for rows.Next() {
var i Document
if err := rows.Scan(
&i.ID,
&i.Type,
&i.Created,
&i.UserID,
&i.DocPageCount,
&i.CmcReference,
&i.PdfFilename,
&i.PdfCreatedAt,
&i.PdfCreatedByUserID,
&i.CmcReference,
&i.ShippingDetails,
&i.Revision,
&i.BillTo,
&i.ShipTo,
&i.EmailSentAt,
&i.EmailSentByUserID,
&i.UserFirstName,
&i.UserLastName,
&i.UserUsername,
&i.PdfCreatorFirstName,
&i.PdfCreatorLastName,
&i.PdfCreatorUsername,
); err != nil {
return nil, err
}
@ -304,92 +317,40 @@ func (q *Queries) ListDocuments(ctx context.Context) ([]ListDocumentsRow, error)
}
const listDocumentsByType = `-- name: ListDocumentsByType :many
SELECT
d.id,
d.type,
d.created,
d.user_id,
d.doc_page_count,
d.pdf_filename,
d.pdf_created_at,
d.pdf_created_by_user_id,
d.cmc_reference,
d.revision,
d.email_sent_at,
d.email_sent_by_user_id,
u.first_name as user_first_name,
u.last_name as user_last_name,
u.username as user_username,
pdf_creator.first_name as pdf_creator_first_name,
pdf_creator.last_name as pdf_creator_last_name,
pdf_creator.username as pdf_creator_username,
COALESCE(ec.name, ic.name, '') as customer_name,
e.title as enquiry_title
FROM documents d
LEFT JOIN users u ON d.user_id = u.id
LEFT JOIN users pdf_creator ON d.pdf_created_by_user_id = pdf_creator.id
LEFT JOIN enquiries e ON d.type IN ('quote', 'orderAck') AND d.cmc_reference = e.title
LEFT JOIN customers ec ON e.customer_id = ec.id
LEFT JOIN invoices i ON d.type = 'invoice' AND d.cmc_reference = i.title
LEFT JOIN customers ic ON i.customer_id = ic.id
WHERE d.type = ?
ORDER BY d.id DESC
LIMIT 1000
SELECT id, type, created, user_id, doc_page_count, cmc_reference, pdf_filename, pdf_created_at, pdf_created_by_user_id, shipping_details, revision, bill_to, ship_to, email_sent_at, email_sent_by_user_id FROM documents WHERE type = ? ORDER BY created DESC LIMIT ? OFFSET ?
`
type ListDocumentsByTypeRow struct {
ID int32 `json:"id"`
type ListDocumentsByTypeParams struct {
Type DocumentsType `json:"type"`
Created time.Time `json:"created"`
UserID int32 `json:"user_id"`
DocPageCount int32 `json:"doc_page_count"`
PdfFilename string `json:"pdf_filename"`
PdfCreatedAt time.Time `json:"pdf_created_at"`
PdfCreatedByUserID int32 `json:"pdf_created_by_user_id"`
CmcReference string `json:"cmc_reference"`
Revision int32 `json:"revision"`
EmailSentAt time.Time `json:"email_sent_at"`
EmailSentByUserID int32 `json:"email_sent_by_user_id"`
UserFirstName sql.NullString `json:"user_first_name"`
UserLastName sql.NullString `json:"user_last_name"`
UserUsername sql.NullString `json:"user_username"`
PdfCreatorFirstName sql.NullString `json:"pdf_creator_first_name"`
PdfCreatorLastName sql.NullString `json:"pdf_creator_last_name"`
PdfCreatorUsername sql.NullString `json:"pdf_creator_username"`
CustomerName string `json:"customer_name"`
EnquiryTitle sql.NullString `json:"enquiry_title"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
func (q *Queries) ListDocumentsByType(ctx context.Context, type_ DocumentsType) ([]ListDocumentsByTypeRow, error) {
rows, err := q.db.QueryContext(ctx, listDocumentsByType, type_)
func (q *Queries) ListDocumentsByType(ctx context.Context, arg ListDocumentsByTypeParams) ([]Document, error) {
rows, err := q.db.QueryContext(ctx, listDocumentsByType, arg.Type, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListDocumentsByTypeRow{}
items := []Document{}
for rows.Next() {
var i ListDocumentsByTypeRow
var i Document
if err := rows.Scan(
&i.ID,
&i.Type,
&i.Created,
&i.UserID,
&i.DocPageCount,
&i.CmcReference,
&i.PdfFilename,
&i.PdfCreatedAt,
&i.PdfCreatedByUserID,
&i.CmcReference,
&i.ShippingDetails,
&i.Revision,
&i.BillTo,
&i.ShipTo,
&i.EmailSentAt,
&i.EmailSentByUserID,
&i.UserFirstName,
&i.UserLastName,
&i.UserUsername,
&i.PdfCreatorFirstName,
&i.PdfCreatorLastName,
&i.PdfCreatorUsername,
&i.CustomerName,
&i.EnquiryTitle,
); err != nil {
return nil, err
}
@ -404,159 +365,12 @@ func (q *Queries) ListDocumentsByType(ctx context.Context, type_ DocumentsType)
return items, nil
}
const searchDocuments = `-- name: SearchDocuments :many
SELECT
d.id,
d.type,
d.created,
d.user_id,
d.doc_page_count,
d.pdf_filename,
d.pdf_created_at,
d.pdf_created_by_user_id,
d.cmc_reference,
d.revision,
d.email_sent_at,
d.email_sent_by_user_id,
u.first_name as user_first_name,
u.last_name as user_last_name,
u.username as user_username,
pdf_creator.first_name as pdf_creator_first_name,
pdf_creator.last_name as pdf_creator_last_name,
pdf_creator.username as pdf_creator_username,
COALESCE(ec.name, ic.name, '') as customer_name,
e.title as enquiry_title
FROM documents d
LEFT JOIN users u ON d.user_id = u.id
LEFT JOIN users pdf_creator ON d.pdf_created_by_user_id = pdf_creator.id
LEFT JOIN enquiries e ON d.type IN ('quote', 'orderAck') AND d.cmc_reference = e.title
LEFT JOIN customers ec ON e.customer_id = ec.id
LEFT JOIN invoices i ON d.type = 'invoice' AND d.cmc_reference = i.title
LEFT JOIN customers ic ON i.customer_id = ic.id
WHERE (
d.pdf_filename LIKE ? OR
d.cmc_reference LIKE ? OR
COALESCE(ec.name, ic.name) LIKE ? OR
e.title LIKE ? OR
u.username LIKE ?
)
ORDER BY d.id DESC
LIMIT 1000
`
type SearchDocumentsParams struct {
PdfFilename string `json:"pdf_filename"`
CmcReference string `json:"cmc_reference"`
Name string `json:"name"`
Title string `json:"title"`
Username string `json:"username"`
}
type SearchDocumentsRow struct {
ID int32 `json:"id"`
Type DocumentsType `json:"type"`
Created time.Time `json:"created"`
UserID int32 `json:"user_id"`
DocPageCount int32 `json:"doc_page_count"`
PdfFilename string `json:"pdf_filename"`
PdfCreatedAt time.Time `json:"pdf_created_at"`
PdfCreatedByUserID int32 `json:"pdf_created_by_user_id"`
CmcReference string `json:"cmc_reference"`
Revision int32 `json:"revision"`
EmailSentAt time.Time `json:"email_sent_at"`
EmailSentByUserID int32 `json:"email_sent_by_user_id"`
UserFirstName sql.NullString `json:"user_first_name"`
UserLastName sql.NullString `json:"user_last_name"`
UserUsername sql.NullString `json:"user_username"`
PdfCreatorFirstName sql.NullString `json:"pdf_creator_first_name"`
PdfCreatorLastName sql.NullString `json:"pdf_creator_last_name"`
PdfCreatorUsername sql.NullString `json:"pdf_creator_username"`
CustomerName string `json:"customer_name"`
EnquiryTitle sql.NullString `json:"enquiry_title"`
}
func (q *Queries) SearchDocuments(ctx context.Context, arg SearchDocumentsParams) ([]SearchDocumentsRow, error) {
rows, err := q.db.QueryContext(ctx, searchDocuments,
arg.PdfFilename,
arg.CmcReference,
arg.Name,
arg.Title,
arg.Username,
)
if err != nil {
return nil, err
}
defer rows.Close()
items := []SearchDocumentsRow{}
for rows.Next() {
var i SearchDocumentsRow
if err := rows.Scan(
&i.ID,
&i.Type,
&i.Created,
&i.UserID,
&i.DocPageCount,
&i.PdfFilename,
&i.PdfCreatedAt,
&i.PdfCreatedByUserID,
&i.CmcReference,
&i.Revision,
&i.EmailSentAt,
&i.EmailSentByUserID,
&i.UserFirstName,
&i.UserLastName,
&i.UserUsername,
&i.PdfCreatorFirstName,
&i.PdfCreatorLastName,
&i.PdfCreatorUsername,
&i.CustomerName,
&i.EnquiryTitle,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const unarchiveDocument = `-- name: UnarchiveDocument :exec
SELECT 1 WHERE ? = ?
`
type UnarchiveDocumentParams struct {
Column1 interface{} `json:"column_1"`
Column2 interface{} `json:"column_2"`
}
// Note: Unarchiving not supported as documents table doesn't have an archived column
// This is a no-op for compatibility
func (q *Queries) UnarchiveDocument(ctx context.Context, arg UnarchiveDocumentParams) error {
_, err := q.db.ExecContext(ctx, unarchiveDocument, arg.Column1, arg.Column2)
return err
}
const updateDocument = `-- name: UpdateDocument :exec
UPDATE documents
SET
type = ?,
user_id = ?,
doc_page_count = ?,
cmc_reference = ?,
pdf_filename = ?,
pdf_created_at = ?,
pdf_created_by_user_id = ?,
shipping_details = ?,
revision = ?,
bill_to = ?,
ship_to = ?,
email_sent_at = ?,
email_sent_by_user_id = ?
UPDATE documents SET
type = ?, user_id = ?, doc_page_count = ?, cmc_reference = ?,
pdf_filename = ?, pdf_created_at = ?, pdf_created_by_user_id = ?,
shipping_details = ?, revision = ?, bill_to = ?, ship_to = ?,
email_sent_at = ?, email_sent_by_user_id = ?
WHERE id = ?
`
@ -596,29 +410,3 @@ func (q *Queries) UpdateDocument(ctx context.Context, arg UpdateDocumentParams)
)
return err
}
const updateDocumentPDFInfo = `-- name: UpdateDocumentPDFInfo :exec
UPDATE documents
SET
pdf_filename = ?,
pdf_created_at = ?,
pdf_created_by_user_id = ?
WHERE id = ?
`
type UpdateDocumentPDFInfoParams struct {
PdfFilename string `json:"pdf_filename"`
PdfCreatedAt time.Time `json:"pdf_created_at"`
PdfCreatedByUserID int32 `json:"pdf_created_by_user_id"`
ID int32 `json:"id"`
}
func (q *Queries) UpdateDocumentPDFInfo(ctx context.Context, arg UpdateDocumentPDFInfoParams) error {
_, err := q.db.ExecContext(ctx, updateDocumentPDFInfo,
arg.PdfFilename,
arg.PdfCreatedAt,
arg.PdfCreatedByUserID,
arg.ID,
)
return err
}

View file

@ -103,6 +103,37 @@ func (q *Queries) GetPurchaseOrder(ctx context.Context, id int32) (PurchaseOrder
return i, err
}
const getPurchaseOrderByDocumentID = `-- name: GetPurchaseOrderByDocumentID :one
SELECT id, issue_date, dispatch_date, date_arrived, title, principle_id, principle_reference, document_id, currency_id, ordered_from, description, dispatch_by, deliver_to, shipping_instructions, jobs_text, freight_forwarder_text, parent_purchase_order_id FROM purchase_orders
WHERE document_id = ?
LIMIT 1
`
func (q *Queries) GetPurchaseOrderByDocumentID(ctx context.Context, documentID int32) (PurchaseOrder, error) {
row := q.db.QueryRowContext(ctx, getPurchaseOrderByDocumentID, documentID)
var i PurchaseOrder
err := row.Scan(
&i.ID,
&i.IssueDate,
&i.DispatchDate,
&i.DateArrived,
&i.Title,
&i.PrincipleID,
&i.PrincipleReference,
&i.DocumentID,
&i.CurrencyID,
&i.OrderedFrom,
&i.Description,
&i.DispatchBy,
&i.DeliverTo,
&i.ShippingInstructions,
&i.JobsText,
&i.FreightForwarderText,
&i.ParentPurchaseOrderID,
)
return i, err
}
const getPurchaseOrderRevisions = `-- name: GetPurchaseOrderRevisions :many
SELECT id, issue_date, dispatch_date, date_arrived, title, principle_id, principle_reference, document_id, currency_id, ordered_from, description, dispatch_by, deliver_to, shipping_instructions, jobs_text, freight_forwarder_text, parent_purchase_order_id FROM purchase_orders
WHERE parent_purchase_order_id = ?

View file

@ -10,9 +10,10 @@ import (
)
type Querier interface {
ArchiveDocument(ctx context.Context, id int32) error
ArchiveEnquiry(ctx context.Context, id int32) error
ArchiveUser(ctx context.Context, id int32) error
CountDocuments(ctx context.Context) (int64, error)
CountDocumentsByType(ctx context.Context, type_ DocumentsType) (int64, error)
CountEnquiries(ctx context.Context) (int64, error)
CountEnquiriesByPrinciple(ctx context.Context, principleCode int32) (int64, error)
CountEnquiriesByPrincipleAndState(ctx context.Context, arg CountEnquiriesByPrincipleAndStateParams) (int64, error)
@ -36,6 +37,7 @@ type Querier interface {
DeleteBox(ctx context.Context, id int32) error
DeleteCountry(ctx context.Context, id int32) error
DeleteCustomer(ctx context.Context, id int32) error
DeleteDocument(ctx context.Context, id int32) error
DeleteLineItem(ctx context.Context, id int32) error
DeleteProduct(ctx context.Context, id int32) error
DeletePurchaseOrder(ctx context.Context, id int32) error
@ -53,7 +55,8 @@ type Querier interface {
GetCustomer(ctx context.Context, id int32) (Customer, error)
GetCustomerAddresses(ctx context.Context, customerID int32) ([]GetCustomerAddressesRow, error)
GetCustomerByABN(ctx context.Context, abn sql.NullString) (Customer, error)
GetDocumentByID(ctx context.Context, id int32) (GetDocumentByIDRow, error)
GetDocument(ctx context.Context, id int32) (Document, error)
GetDocumentWithUser(ctx context.Context, id int32) (GetDocumentWithUserRow, error)
GetEnquiriesByCustomer(ctx context.Context, arg GetEnquiriesByCustomerParams) ([]GetEnquiriesByCustomerRow, error)
GetEnquiriesByUser(ctx context.Context, arg GetEnquiriesByUserParams) ([]GetEnquiriesByUserRow, error)
GetEnquiry(ctx context.Context, id int32) (GetEnquiryRow, error)
@ -67,9 +70,10 @@ type Querier interface {
GetProductByItemCode(ctx context.Context, itemCode string) (Product, error)
GetProductsByCategory(ctx context.Context, arg GetProductsByCategoryParams) ([]Product, error)
GetPurchaseOrder(ctx context.Context, id int32) (PurchaseOrder, error)
GetPurchaseOrderByDocumentID(ctx context.Context, id int32) (GetPurchaseOrderByDocumentIDRow, error)
GetPurchaseOrderByDocumentID(ctx context.Context, documentID int32) (PurchaseOrder, error)
GetPurchaseOrderRevisions(ctx context.Context, parentPurchaseOrderID int32) ([]PurchaseOrder, error)
GetPurchaseOrdersByPrinciple(ctx context.Context, arg GetPurchaseOrdersByPrincipleParams) ([]PurchaseOrder, error)
GetRecentDocuments(ctx context.Context, limit int32) ([]GetRecentDocumentsRow, error)
GetState(ctx context.Context, id int32) (State, error)
GetStatus(ctx context.Context, id int32) (Status, error)
GetUser(ctx context.Context, id int32) (GetUserRow, error)
@ -84,8 +88,8 @@ type Querier interface {
ListBoxesByShipment(ctx context.Context, shipmentID int32) ([]Box, error)
ListCountries(ctx context.Context, arg ListCountriesParams) ([]Country, error)
ListCustomers(ctx context.Context, arg ListCustomersParams) ([]Customer, error)
ListDocuments(ctx context.Context) ([]ListDocumentsRow, error)
ListDocumentsByType(ctx context.Context, type_ DocumentsType) ([]ListDocumentsByTypeRow, error)
ListDocuments(ctx context.Context, arg ListDocumentsParams) ([]Document, error)
ListDocumentsByType(ctx context.Context, arg ListDocumentsByTypeParams) ([]Document, error)
ListEnquiries(ctx context.Context, arg ListEnquiriesParams) ([]ListEnquiriesRow, error)
ListLineItems(ctx context.Context, arg ListLineItemsParams) ([]LineItem, error)
ListLineItemsByDocument(ctx context.Context, documentID int32) ([]LineItem, error)
@ -97,13 +101,9 @@ type Querier interface {
MarkEnquirySubmitted(ctx context.Context, arg MarkEnquirySubmittedParams) error
SearchCountriesByName(ctx context.Context, concat interface{}) ([]Country, error)
SearchCustomersByName(ctx context.Context, arg SearchCustomersByNameParams) ([]Customer, error)
SearchDocuments(ctx context.Context, arg SearchDocumentsParams) ([]SearchDocumentsRow, error)
SearchEnquiries(ctx context.Context, arg SearchEnquiriesParams) ([]SearchEnquiriesRow, error)
SearchProductsByTitle(ctx context.Context, arg SearchProductsByTitleParams) ([]Product, error)
SearchPurchaseOrdersByTitle(ctx context.Context, arg SearchPurchaseOrdersByTitleParams) ([]PurchaseOrder, error)
// Note: Unarchiving not supported as documents table doesn't have an archived column
// This is a no-op for compatibility
UnarchiveDocument(ctx context.Context, arg UnarchiveDocumentParams) error
UnarchiveEnquiry(ctx context.Context, id int32) error
UnarchiveUser(ctx context.Context, id int32) error
UpdateAddress(ctx context.Context, arg UpdateAddressParams) error
@ -112,7 +112,6 @@ type Querier interface {
UpdateCountry(ctx context.Context, arg UpdateCountryParams) error
UpdateCustomer(ctx context.Context, arg UpdateCustomerParams) error
UpdateDocument(ctx context.Context, arg UpdateDocumentParams) error
UpdateDocumentPDFInfo(ctx context.Context, arg UpdateDocumentPDFInfoParams) error
UpdateEnquiry(ctx context.Context, arg UpdateEnquiryParams) error
UpdateEnquiryStatus(ctx context.Context, arg UpdateEnquiryStatusParams) error
UpdateLineItem(ctx context.Context, arg UpdateLineItemParams) error

View file

@ -31,14 +31,35 @@ func (h *DocumentHandler) List(w http.ResponseWriter, r *http.Request) {
// Check for type filter
docType := r.URL.Query().Get("type")
// Get pagination parameters
limit := int32(20)
offset := int32(0)
if l := r.URL.Query().Get("limit"); l != "" {
if val, err := strconv.Atoi(l); err == nil && val > 0 {
limit = int32(val)
}
}
if o := r.URL.Query().Get("offset"); o != "" {
if val, err := strconv.Atoi(o); err == nil && val >= 0 {
offset = int32(val)
}
}
var documents interface{}
var err error
if docType != "" {
// Convert string to DocumentsType enum
documents, err = h.queries.ListDocumentsByType(ctx, db.DocumentsType(docType))
documents, err = h.queries.ListDocumentsByType(ctx, db.ListDocumentsByTypeParams{
Type: db.DocumentsType(docType),
Limit: limit,
Offset: offset,
})
} else {
documents, err = h.queries.ListDocuments(ctx)
documents, err = h.queries.ListDocuments(ctx, db.ListDocumentsParams{
Limit: limit,
Offset: offset,
})
}
if err != nil {
@ -85,7 +106,7 @@ func (h *DocumentHandler) Get(w http.ResponseWriter, r *http.Request) {
}
ctx := context.Background()
document, err := h.queries.GetDocumentByID(ctx, int32(id))
document, err := h.queries.GetDocument(ctx, int32(id))
if err != nil {
log.Printf("Error fetching document %d: %v", id, err)
http.Error(w, "Document not found", http.StatusNotFound)
@ -272,82 +293,20 @@ func (h *DocumentHandler) Update(w http.ResponseWriter, r *http.Request) {
// Archive handles PUT /api/documents/{id}/archive
func (h *DocumentHandler) Archive(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
idStr := vars["id"]
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
http.Error(w, "Invalid document ID", http.StatusBadRequest)
return
}
ctx := context.Background()
err = h.queries.ArchiveDocument(ctx, int32(id))
if err != nil {
log.Printf("Error archiving document %d: %v", id, err)
http.Error(w, "Failed to archive document", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "Document archived successfully",
})
// TODO: Implement archive functionality when query is added
http.Error(w, "Archive functionality not yet implemented", http.StatusNotImplemented)
}
// Unarchive handles PUT /api/documents/{id}/unarchive
func (h *DocumentHandler) Unarchive(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
idStr := vars["id"]
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
http.Error(w, "Invalid document ID", http.StatusBadRequest)
return
}
ctx := context.Background()
err = h.queries.UnarchiveDocument(ctx, db.UnarchiveDocumentParams{
Column1: int32(id),
Column2: int32(id),
})
if err != nil {
log.Printf("Error unarchiving document %d: %v", id, err)
http.Error(w, "Failed to unarchive document", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "Document unarchived successfully",
})
// TODO: Implement unarchive functionality when query is added
http.Error(w, "Unarchive functionality not yet implemented", http.StatusNotImplemented)
}
// Search handles GET /api/documents/search?q=query
func (h *DocumentHandler) Search(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("q")
if query == "" {
http.Error(w, "Search query is required", http.StatusBadRequest)
return
}
ctx := context.Background()
searchPattern := "%" + query + "%"
documents, err := h.queries.SearchDocuments(ctx, db.SearchDocumentsParams{
PdfFilename: searchPattern,
CmcReference: searchPattern,
Name: searchPattern,
Title: searchPattern,
Username: searchPattern,
})
if err != nil {
log.Printf("Error searching documents: %v", err)
http.Error(w, "Failed to search documents", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(documents)
// TODO: Implement search functionality when query is added
http.Error(w, "Search functionality not yet implemented", http.StatusNotImplemented)
}
// GeneratePDF handles GET /documents/pdf/{id}
@ -364,7 +323,7 @@ func (h *DocumentHandler) GeneratePDF(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
// Get document
document, err := h.queries.GetDocumentByID(ctx, int32(id))
document, err := h.queries.GetDocument(ctx, int32(id))
if err != nil {
log.Printf("Error fetching document %d: %v", id, err)
http.Error(w, "Document not found", http.StatusNotFound)
@ -488,18 +447,9 @@ func (h *DocumentHandler) GeneratePDF(w http.ResponseWriter, r *http.Request) {
return
}
// Update document with PDF filename and timestamp
now := time.Now()
err = h.queries.UpdateDocumentPDFInfo(ctx, db.UpdateDocumentPDFInfoParams{
PdfFilename: filename,
PdfCreatedAt: now,
PdfCreatedByUserID: 1, // TODO: Get current user ID
ID: int32(id),
})
if err != nil {
log.Printf("Error updating document PDF info: %v", err)
// Don't fail the request, PDF was generated successfully
}
// TODO: Update document with PDF filename and timestamp
// This would require a specific query to update just PDF info
log.Printf("PDF generated successfully: %s", filename)
// Return success response with redirect to document view
w.Header().Set("Content-Type", "text/html")
@ -516,3 +466,100 @@ func (h *DocumentHandler) GeneratePDF(w http.ResponseWriter, r *http.Request) {
</html>
`, id, id)
}
// GetRecentActivity returns recent documents as HTML for the dashboard
func (h *DocumentHandler) GetRecentActivity(w http.ResponseWriter, r *http.Request) {
// Get the last 10 documents
documents, err := h.queries.GetRecentDocuments(r.Context(), 10)
if err != nil {
log.Printf("Error fetching recent activity: %v", err)
http.Error(w, "Failed to fetch recent activity", http.StatusInternalServerError)
return
}
// Format the response as HTML
w.Header().Set("Content-Type", "text/html")
if len(documents) == 0 {
fmt.Fprintf(w, `<div class="has-text-centered has-text-grey">
<p>No recent activity</p>
</div>`)
return
}
// Build HTML table
fmt.Fprintf(w, `<table class="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>Document</th>
<th>Reference</th>
<th>Created By</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody>`)
for _, doc := range documents {
// Format the document type badge
var typeClass string
switch doc.Type {
case "quote":
typeClass = "is-info"
case "invoice":
typeClass = "is-success"
case "purchaseOrder":
typeClass = "is-warning"
case "orderAck":
typeClass = "is-primary"
case "packingList":
typeClass = "is-link"
default:
typeClass = "is-light"
}
// Format the date
createdDate := doc.Created.Format("Jan 2, 2006 3:04 PM")
// Handle null username
createdBy := "Unknown"
if doc.CreatedByUsername.Valid {
createdBy = doc.CreatedByUsername.String
}
// Add revision indicator if applicable
revisionText := ""
if doc.Revision > 0 {
revisionText = fmt.Sprintf(" <span class=\"tag is-light\">Rev %d</span>", doc.Revision)
}
fmt.Fprintf(w, `<tr>
<td>
<span class="tag %s">%s</span>
</td>
<td>%s%s</td>
<td>%s</td>
<td><small>%s</small></td>
<td>
<a href="/documents/%d" class="button is-small is-outlined">
<span class="icon is-small">
<i class="fas fa-eye"></i>
</span>
<span>View</span>
</a>
</td>
</tr>`, typeClass, doc.DisplayName, doc.CmcReference, revisionText, createdBy, createdDate, doc.ID)
}
fmt.Fprintf(w, `</tbody></table>`)
// Add a link to view all documents
fmt.Fprintf(w, `<div class="has-text-centered mt-4">
<a href="/documents" class="button is-link is-outlined">
<span>View All Documents</span>
<span class="icon is-small">
<i class="fas fa-arrow-right"></i>
</span>
</a>
</div>`)
}

View file

@ -615,13 +615,23 @@ func (h *PageHandler) DocumentsIndex(w http.ResponseWriter, r *http.Request) {
// Get document type filter
docType := r.URL.Query().Get("type")
limit := 20
offset := (page - 1) * limit
var documents interface{}
var err error
if docType != "" {
documents, err = h.queries.ListDocumentsByType(r.Context(), db.DocumentsType(docType))
documents, err = h.queries.ListDocumentsByType(r.Context(), db.ListDocumentsByTypeParams{
Type: db.DocumentsType(docType),
Limit: int32(limit + 1),
Offset: int32(offset),
})
} else {
documents, err = h.queries.ListDocuments(r.Context())
documents, err = h.queries.ListDocuments(r.Context(), db.ListDocumentsParams{
Limit: int32(limit + 1),
Offset: int32(offset),
})
}
if err != nil {
@ -664,7 +674,7 @@ func (h *PageHandler) DocumentsShow(w http.ResponseWriter, r *http.Request) {
return
}
document, err := h.queries.GetDocumentByID(r.Context(), int32(id))
document, err := h.queries.GetDocumentWithUser(r.Context(), int32(id))
if err != nil {
log.Printf("Error fetching document %d: %v", id, err)
http.Error(w, "Document not found", http.StatusNotFound)
@ -681,32 +691,23 @@ func (h *PageHandler) DocumentsShow(w http.ResponseWriter, r *http.Request) {
}
func (h *PageHandler) DocumentsSearch(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("search")
// query := r.URL.Query().Get("search") // TODO: Use when search is implemented
var documents interface{}
var err error
if query == "" {
// If no search query, return regular list
documents, err = h.queries.ListDocuments(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else {
searchPattern := "%" + query + "%"
documents, err = h.queries.SearchDocuments(r.Context(), db.SearchDocumentsParams{
PdfFilename: searchPattern,
CmcReference: searchPattern,
Name: searchPattern,
Title: searchPattern,
Username: searchPattern,
// For now, just return all documents until search is implemented
limit := 20
offset := 0
documents, err = h.queries.ListDocuments(r.Context(), db.ListDocumentsParams{
Limit: int32(limit),
Offset: int32(offset),
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
data := map[string]interface{}{
"Documents": documents,
@ -726,7 +727,7 @@ func (h *PageHandler) DocumentsView(w http.ResponseWriter, r *http.Request) {
return
}
document, err := h.queries.GetDocumentByID(r.Context(), int32(id))
document, err := h.queries.GetDocumentWithUser(r.Context(), int32(id))
if err != nil {
http.Error(w, "Document not found", http.StatusNotFound)
return

View file

@ -43,9 +43,9 @@ func (g *Generator) Page1Header() {
g.pdf.SetTextColor(0, 0, 152)
// Add logo if available (assuming logo is in static directory)
// logoPath := filepath.Join("static", "images", "cmclogosmall.png")
logoPath := filepath.Join("static", "images", "cmclogosmall.png")
// Try to add logo, but don't fail if it doesn't exist or isn't a proper image
// g.pdf.ImageOptions(logoPath, 10, 10, 0, 28, false, gofpdf.ImageOptions{ImageType: "PNG"}, 0, "http://www.cmctechnologies.com.au")
g.pdf.ImageOptions(logoPath, 10, 10, 0, 28, false, gofpdf.ImageOptions{ImageType: "PNG"}, 0, "http://www.cmctechnologies.com.au")
// Company name
g.pdf.SetFont("Helvetica", "B", 30)

View file

@ -9,7 +9,7 @@ import (
// QuotePDFData contains all data needed to generate a quote PDF
type QuotePDFData struct {
Document *db.GetDocumentByIDRow
Document *db.Document
Quote interface{} // Quote specific data
Enquiry *db.Enquiry
Customer *db.Customer
@ -32,25 +32,26 @@ func GenerateQuotePDF(data *QuotePDFData, outputDir string) (string, error) {
gen.Page1Header()
// Extract data for details box
companyName := data.Document.CustomerName
companyName := "" // TODO: Get from customer data
if data.Customer != nil {
companyName = data.Customer.Name
}
emailTo := "" // TODO: Get from contact
attention := "" // TODO: Get from contact
fromName := fmt.Sprintf("%s %s", data.User.FirstName, data.User.LastName)
fromEmail := data.User.Email
enquiryNumber := ""
if data.Document.EnquiryTitle.Valid {
enquiryNumber = data.Document.EnquiryTitle.String
}
// Use CMC reference as the quote number
quoteNumber := data.Document.CmcReference
if data.Document.Revision > 0 {
enquiryNumber = fmt.Sprintf("%s.%d", enquiryNumber, data.Document.Revision)
quoteNumber = fmt.Sprintf("%s.%d", quoteNumber, data.Document.Revision)
}
yourReference := fmt.Sprintf("Enquiry on %s", data.Document.Created.Format("2 Jan 2006"))
issueDate := time.Now().Format("2 January 2006")
issueDate := data.Document.Created.Format("2 January 2006")
// Add details box
gen.DetailsBox("QUOTE", companyName, emailTo, attention, fromName, fromEmail, enquiryNumber, yourReference, issueDate)
gen.DetailsBox("QUOTE", companyName, emailTo, attention, fromName, fromEmail, quoteNumber, yourReference, issueDate)
// Add page content if any
// TODO: Add document pages content
@ -101,11 +102,11 @@ func GenerateQuotePDF(data *QuotePDFData, outputDir string) (string, error) {
// TODO: Add terms and conditions page
// Generate filename
filename := enquiryNumber
filename := quoteNumber
if data.Document.Revision > 0 {
filename = fmt.Sprintf("%s_%d.pdf", enquiryNumber, data.Document.Revision)
filename = fmt.Sprintf("%s_%d.pdf", quoteNumber, data.Document.Revision)
} else {
filename = fmt.Sprintf("%s.pdf", enquiryNumber)
filename = fmt.Sprintf("%s.pdf", quoteNumber)
}
// Save PDF
@ -213,8 +214,8 @@ func GenerateInvoicePDF(data *InvoicePDFData, outputDir string) (string, error)
// PurchaseOrderPDFData contains all data needed to generate a purchase order PDF
type PurchaseOrderPDFData struct {
Document *db.GetDocumentByIDRow
PurchaseOrder *db.GetPurchaseOrderByDocumentIDRow
Document *db.Document
PurchaseOrder *db.PurchaseOrder
Principle *db.Principle
LineItems []db.GetLineItemsTableRow
Currency interface{} // Currency data

Binary file not shown.

View file

@ -1,201 +1,85 @@
-- name: GetDocument :one
SELECT * FROM documents WHERE id = ?;
-- name: ListDocuments :many
SELECT
d.id,
d.type,
d.created,
d.user_id,
d.doc_page_count,
d.pdf_filename,
d.pdf_created_at,
d.pdf_created_by_user_id,
d.cmc_reference,
d.revision,
d.email_sent_at,
d.email_sent_by_user_id,
u.first_name as user_first_name,
u.last_name as user_last_name,
u.username as user_username,
pdf_creator.first_name as pdf_creator_first_name,
pdf_creator.last_name as pdf_creator_last_name,
pdf_creator.username as pdf_creator_username
FROM documents d
LEFT JOIN users u ON d.user_id = u.id
LEFT JOIN users pdf_creator ON d.pdf_created_by_user_id = pdf_creator.id
ORDER BY d.id DESC
LIMIT 1000;
SELECT * FROM documents ORDER BY created DESC LIMIT ? OFFSET ?;
-- name: ListDocumentsByType :many
SELECT * FROM documents WHERE type = ? ORDER BY created DESC LIMIT ? OFFSET ?;
-- name: CreateDocument :execresult
INSERT INTO documents (
type, created, user_id, doc_page_count, cmc_reference,
pdf_filename, pdf_created_at, pdf_created_by_user_id,
shipping_details, revision, bill_to, ship_to,
email_sent_at, email_sent_by_user_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
-- name: UpdateDocument :exec
UPDATE documents SET
type = ?, user_id = ?, doc_page_count = ?, cmc_reference = ?,
pdf_filename = ?, pdf_created_at = ?, pdf_created_by_user_id = ?,
shipping_details = ?, revision = ?, bill_to = ?, ship_to = ?,
email_sent_at = ?, email_sent_by_user_id = ?
WHERE id = ?;
-- name: DeleteDocument :exec
DELETE FROM documents WHERE id = ?;
-- name: GetRecentDocuments :many
SELECT
d.id,
d.type,
d.created,
d.user_id,
d.doc_page_count,
d.pdf_filename,
d.pdf_created_at,
d.pdf_created_by_user_id,
d.cmc_reference,
d.pdf_filename,
d.revision,
d.email_sent_at,
d.email_sent_by_user_id,
u.first_name as user_first_name,
u.last_name as user_last_name,
u.username as user_username,
pdf_creator.first_name as pdf_creator_first_name,
pdf_creator.last_name as pdf_creator_last_name,
pdf_creator.username as pdf_creator_username,
COALESCE(ec.name, ic.name, '') as customer_name,
e.title as enquiry_title
u.username as created_by_username,
CASE
WHEN d.type = 'quote' THEN CONCAT('Quote ', d.cmc_reference)
WHEN d.type = 'invoice' THEN CONCAT('Invoice ', d.cmc_reference)
WHEN d.type = 'purchaseOrder' THEN CONCAT('Purchase Order ', d.cmc_reference)
WHEN d.type = 'orderAck' THEN CONCAT('Order Ack ', d.cmc_reference)
WHEN d.type = 'packingList' THEN CONCAT('Packing List ', d.cmc_reference)
ELSE CONCAT(d.type, ' ', d.cmc_reference)
END as display_name
FROM documents d
LEFT JOIN users u ON d.user_id = u.id
LEFT JOIN users pdf_creator ON d.pdf_created_by_user_id = pdf_creator.id
LEFT JOIN enquiries e ON d.type IN ('quote', 'orderAck') AND d.cmc_reference = e.title
LEFT JOIN customers ec ON e.customer_id = ec.id
LEFT JOIN invoices i ON d.type = 'invoice' AND d.cmc_reference = i.title
LEFT JOIN customers ic ON i.customer_id = ic.id
WHERE d.type = ?
ORDER BY d.id DESC
LIMIT 1000;
ORDER BY d.created DESC
LIMIT ?;
-- name: GetDocumentByID :one
-- name: CountDocuments :one
SELECT COUNT(*) FROM documents;
-- name: CountDocumentsByType :one
SELECT COUNT(*) FROM documents WHERE type = ?;
-- name: GetDocumentWithUser :one
SELECT
d.id,
d.type,
d.created,
d.user_id,
d.doc_page_count,
d.cmc_reference,
d.pdf_filename,
d.pdf_created_at,
d.pdf_created_by_user_id,
d.cmc_reference,
d.revision,
d.shipping_details,
d.revision,
d.bill_to,
d.ship_to,
d.email_sent_at,
d.email_sent_by_user_id,
u.username as user_username,
u.first_name as user_first_name,
u.last_name as user_last_name,
u.username as user_username,
pdf_creator.first_name as pdf_creator_first_name,
pdf_creator.last_name as pdf_creator_last_name,
pdf_creator.username as pdf_creator_username,
COALESCE(ec.name, ic.name, '') as customer_name,
e.title as enquiry_title
u.email as user_email,
pu.username as pdf_creator_username,
pu.first_name as pdf_creator_first_name,
pu.last_name as pdf_creator_last_name,
pu.email as pdf_creator_email
FROM documents d
LEFT JOIN users u ON d.user_id = u.id
LEFT JOIN users pdf_creator ON d.pdf_created_by_user_id = pdf_creator.id
LEFT JOIN enquiries e ON d.type IN ('quote', 'orderAck') AND d.cmc_reference = e.title
LEFT JOIN customers ec ON e.customer_id = ec.id
LEFT JOIN invoices i ON d.type = 'invoice' AND d.cmc_reference = i.title
LEFT JOIN customers ic ON i.customer_id = ic.id
LEFT JOIN users pu ON d.pdf_created_by_user_id = pu.id
WHERE d.id = ?;
-- name: CreateDocument :execresult
INSERT INTO documents (
type,
created,
user_id,
doc_page_count,
cmc_reference,
pdf_filename,
pdf_created_at,
pdf_created_by_user_id,
shipping_details,
revision,
bill_to,
ship_to,
email_sent_at,
email_sent_by_user_id
) VALUES (
?, NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
);
-- name: UpdateDocument :exec
UPDATE documents
SET
type = ?,
user_id = ?,
doc_page_count = ?,
cmc_reference = ?,
pdf_filename = ?,
pdf_created_at = ?,
pdf_created_by_user_id = ?,
shipping_details = ?,
revision = ?,
bill_to = ?,
ship_to = ?,
email_sent_at = ?,
email_sent_by_user_id = ?
WHERE id = ?;
-- name: ArchiveDocument :exec
DELETE FROM documents
WHERE id = ?;
-- name: UnarchiveDocument :exec
-- Note: Unarchiving not supported as documents table doesn't have an archived column
-- This is a no-op for compatibility
SELECT 1 WHERE ? = ?;
-- name: SearchDocuments :many
SELECT
d.id,
d.type,
d.created,
d.user_id,
d.doc_page_count,
d.pdf_filename,
d.pdf_created_at,
d.pdf_created_by_user_id,
d.cmc_reference,
d.revision,
d.email_sent_at,
d.email_sent_by_user_id,
u.first_name as user_first_name,
u.last_name as user_last_name,
u.username as user_username,
pdf_creator.first_name as pdf_creator_first_name,
pdf_creator.last_name as pdf_creator_last_name,
pdf_creator.username as pdf_creator_username,
COALESCE(ec.name, ic.name, '') as customer_name,
e.title as enquiry_title
FROM documents d
LEFT JOIN users u ON d.user_id = u.id
LEFT JOIN users pdf_creator ON d.pdf_created_by_user_id = pdf_creator.id
LEFT JOIN enquiries e ON d.type IN ('quote', 'orderAck') AND d.cmc_reference = e.title
LEFT JOIN customers ec ON e.customer_id = ec.id
LEFT JOIN invoices i ON d.type = 'invoice' AND d.cmc_reference = i.title
LEFT JOIN customers ic ON i.customer_id = ic.id
WHERE (
d.pdf_filename LIKE ? OR
d.cmc_reference LIKE ? OR
COALESCE(ec.name, ic.name) LIKE ? OR
e.title LIKE ? OR
u.username LIKE ?
)
ORDER BY d.id DESC
LIMIT 1000;
-- name: UpdateDocumentPDFInfo :exec
UPDATE documents
SET
pdf_filename = ?,
pdf_created_at = ?,
pdf_created_by_user_id = ?
WHERE id = ?;
-- name: GetPurchaseOrderByDocumentID :one
SELECT
po.id,
po.title,
po.principle_id,
po.principle_reference,
po.issue_date,
po.ordered_from,
po.dispatch_by,
po.deliver_to,
po.shipping_instructions
FROM purchase_orders po
JOIN documents d ON d.cmc_reference = po.title
WHERE d.id = ? AND d.type = 'purchaseOrder';

View file

@ -58,3 +58,8 @@ SELECT * FROM purchase_orders
WHERE title LIKE CONCAT('%', ?, '%')
ORDER BY issue_date DESC
LIMIT ? OFFSET ?;
-- name: GetPurchaseOrderByDocumentID :one
SELECT * FROM purchase_orders
WHERE document_id = ?
LIMIT 1;

View file

@ -109,7 +109,8 @@
</div>
{{end}}
{{if or .Document.CustomerName .Document.EnquiryTitle.Valid}}
{{/* TODO: Add customer and enquiry information when queries are available */}}
{{if false}}
<div class="box">
<h3 class="title is-5">Related Information</h3>
<table class="table is-fullwidth">