Deepen all business modules: methods, validations, workflows
Account: - action_post: partner validation, line count check, sequence number assignment (JOURNAL/YYYY/NNNN format) - action_register_payment: opens payment wizard from invoice - remove_move_reconcile: undo reconciliation, reset residuals - Register Payment button in invoice form (visible when posted+unpaid) Sale: - action_cancel: cancels linked draft invoices + SO state - action_draft: reset cancelled SO to draft - action_view_invoice: navigate to linked invoices - Cancel/Reset buttons in form view header Purchase: - button_draft: reset cancelled PO to draft - action_create_bill already existed Stock: - action_cancel on picking: cancels moves + picking state CRM: - action_set_won_rainbowman: sets Won stage + rainbow effect - convert_opportunity: lead→opportunity type switch HR: - hr.contract model (name, employee, wage, dates, state) Project: - action_blocked on task (kanban_state) - Task stage seed data (New, In Progress, Done, Cancelled) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -205,6 +205,25 @@ func initAccountMove() {
|
|||||||
if state != "draft" {
|
if state != "draft" {
|
||||||
return nil, fmt.Errorf("account: can only post draft entries (current: %s)", state)
|
return nil, fmt.Errorf("account: can only post draft entries (current: %s)", state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check partner is set for invoice types
|
||||||
|
var moveType string
|
||||||
|
env.Tx().QueryRow(env.Ctx(), `SELECT move_type FROM account_move WHERE id = $1`, id).Scan(&moveType)
|
||||||
|
if moveType != "entry" {
|
||||||
|
var partnerID *int64
|
||||||
|
env.Tx().QueryRow(env.Ctx(), `SELECT partner_id FROM account_move WHERE id = $1`, id).Scan(&partnerID)
|
||||||
|
if partnerID == nil || *partnerID == 0 {
|
||||||
|
return nil, fmt.Errorf("account: invoice requires a partner")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check at least one line exists
|
||||||
|
var lineCount int
|
||||||
|
env.Tx().QueryRow(env.Ctx(), `SELECT count(*) FROM account_move_line WHERE move_id = $1`, id).Scan(&lineCount)
|
||||||
|
if lineCount == 0 {
|
||||||
|
return nil, fmt.Errorf("account: cannot post an entry with no lines")
|
||||||
|
}
|
||||||
|
|
||||||
// Check balanced
|
// Check balanced
|
||||||
var debitSum, creditSum float64
|
var debitSum, creditSum float64
|
||||||
env.Tx().QueryRow(env.Ctx(),
|
env.Tx().QueryRow(env.Ctx(),
|
||||||
@@ -214,6 +233,34 @@ func initAccountMove() {
|
|||||||
if diff < -0.005 || diff > 0.005 {
|
if diff < -0.005 || diff > 0.005 {
|
||||||
return nil, fmt.Errorf("account: cannot post unbalanced entry (debit=%.2f, credit=%.2f)", debitSum, creditSum)
|
return nil, fmt.Errorf("account: cannot post unbalanced entry (debit=%.2f, credit=%.2f)", debitSum, creditSum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign sequence number if name is still "/"
|
||||||
|
var name string
|
||||||
|
env.Tx().QueryRow(env.Ctx(), `SELECT name FROM account_move WHERE id = $1`, id).Scan(&name)
|
||||||
|
if name == "/" || name == "" {
|
||||||
|
// Generate sequence number
|
||||||
|
var journalID int64
|
||||||
|
var journalCode string
|
||||||
|
env.Tx().QueryRow(env.Ctx(),
|
||||||
|
`SELECT j.id, j.code FROM account_journal j
|
||||||
|
JOIN account_move m ON m.journal_id = j.id WHERE m.id = $1`, id,
|
||||||
|
).Scan(&journalID, &journalCode)
|
||||||
|
|
||||||
|
// Get next sequence number
|
||||||
|
var nextNum int64
|
||||||
|
env.Tx().QueryRow(env.Ctx(),
|
||||||
|
`SELECT COALESCE(MAX(sequence_number), 0) + 1 FROM account_move WHERE journal_id = $1`,
|
||||||
|
journalID).Scan(&nextNum)
|
||||||
|
|
||||||
|
// Format: journalCode/YYYY/NNNN
|
||||||
|
year := time.Now().Format("2006")
|
||||||
|
newName := fmt.Sprintf("%s/%s/%04d", journalCode, year, nextNum)
|
||||||
|
|
||||||
|
env.Tx().Exec(env.Ctx(),
|
||||||
|
`UPDATE account_move SET name = $1, sequence_number = $2 WHERE id = $3`,
|
||||||
|
newName, nextNum, id)
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := env.Tx().Exec(env.Ctx(),
|
if _, err := env.Tx().Exec(env.Ctx(),
|
||||||
`UPDATE account_move SET state = 'posted' WHERE id = $1`, id); err != nil {
|
`UPDATE account_move SET state = 'posted' WHERE id = $1`, id); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -255,6 +302,23 @@ func initAccountMove() {
|
|||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// action_register_payment: opens the payment register wizard.
|
||||||
|
// Mirrors: odoo/addons/account/models/account_move.py action_register_payment()
|
||||||
|
m.RegisterMethod("action_register_payment", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"type": "ir.actions.act_window",
|
||||||
|
"name": "Register Payment",
|
||||||
|
"res_model": "account.payment.register",
|
||||||
|
"view_mode": "form",
|
||||||
|
"views": [][]interface{}{{nil, "form"}},
|
||||||
|
"target": "new",
|
||||||
|
"context": map[string]interface{}{
|
||||||
|
"active_model": "account.move",
|
||||||
|
"active_ids": rs.IDs(),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
|
||||||
// -- Business Method: create_invoice_with_tax --
|
// -- Business Method: create_invoice_with_tax --
|
||||||
// Creates a customer invoice with automatic tax line generation.
|
// Creates a customer invoice with automatic tax line generation.
|
||||||
// For each product line that carries a tax_id, a separate tax line
|
// For each product line that carries a tax_id, a separate tax line
|
||||||
@@ -929,6 +993,32 @@ func initAccountMoveLine() {
|
|||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// remove_move_reconcile: undo reconciliation on selected lines.
|
||||||
|
// Mirrors: odoo/addons/account/models/account_move_line.py remove_move_reconcile()
|
||||||
|
m.RegisterMethod("remove_move_reconcile", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
env := rs.Env()
|
||||||
|
for _, lineID := range rs.IDs() {
|
||||||
|
// Find partial reconciles involving this line
|
||||||
|
env.Tx().Exec(env.Ctx(),
|
||||||
|
`DELETE FROM account_partial_reconcile WHERE debit_move_id = $1 OR credit_move_id = $1`, lineID)
|
||||||
|
// Reset residual to balance
|
||||||
|
env.Tx().Exec(env.Ctx(),
|
||||||
|
`UPDATE account_move_line SET amount_residual = balance, full_reconcile_id = NULL WHERE id = $1`, lineID)
|
||||||
|
}
|
||||||
|
// Clean up orphaned full reconciles
|
||||||
|
env.Tx().Exec(env.Ctx(),
|
||||||
|
`DELETE FROM account_full_reconcile WHERE id NOT IN (SELECT DISTINCT full_reconcile_id FROM account_partial_reconcile WHERE full_reconcile_id IS NOT NULL)`)
|
||||||
|
// Update payment states
|
||||||
|
for _, lineID := range rs.IDs() {
|
||||||
|
var moveID int64
|
||||||
|
env.Tx().QueryRow(env.Ctx(), `SELECT move_id FROM account_move_line WHERE id = $1`, lineID).Scan(&moveID)
|
||||||
|
if moveID > 0 {
|
||||||
|
updatePaymentState(env, moveID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// initAccountPayment registers account.payment.
|
// initAccountPayment registers account.payment.
|
||||||
|
|||||||
@@ -105,6 +105,38 @@ func initCRMLead() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// convert_opportunity: alias for convert_to_opportunity
|
||||||
|
m.RegisterMethod("convert_opportunity", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
env := rs.Env()
|
||||||
|
for _, id := range rs.IDs() {
|
||||||
|
env.Tx().Exec(env.Ctx(),
|
||||||
|
`UPDATE crm_lead SET type = 'opportunity' WHERE id = $1`, id)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// action_set_won_rainbowman: set won stage + rainbow effect
|
||||||
|
m.RegisterMethod("action_set_won_rainbowman", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
env := rs.Env()
|
||||||
|
// Find Won stage
|
||||||
|
var wonStageID int64
|
||||||
|
env.Tx().QueryRow(env.Ctx(),
|
||||||
|
`SELECT id FROM crm_stage WHERE is_won = true LIMIT 1`).Scan(&wonStageID)
|
||||||
|
if wonStageID == 0 {
|
||||||
|
wonStageID = 4 // fallback
|
||||||
|
}
|
||||||
|
for _, id := range rs.IDs() {
|
||||||
|
env.Tx().Exec(env.Ctx(),
|
||||||
|
`UPDATE crm_lead SET stage_id = $1, probability = 100 WHERE id = $2`, wonStageID, id)
|
||||||
|
}
|
||||||
|
return map[string]interface{}{
|
||||||
|
"effect": map[string]interface{}{
|
||||||
|
"type": "rainbow_man",
|
||||||
|
"message": "Congrats, you won this opportunity!",
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// initCRMStage registers the crm.stage model.
|
// initCRMStage registers the crm.stage model.
|
||||||
|
|||||||
31
addons/hr/models/hr_contract.go
Normal file
31
addons/hr/models/hr_contract.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "odoo-go/pkg/orm"
|
||||||
|
|
||||||
|
// initHrContract registers the hr.contract model.
|
||||||
|
// Mirrors: odoo/addons/hr_contract/models/hr_contract.py
|
||||||
|
func initHrContract() {
|
||||||
|
m := orm.NewModel("hr.contract", orm.ModelOpts{
|
||||||
|
Description: "Employee Contract",
|
||||||
|
Order: "date_start desc",
|
||||||
|
})
|
||||||
|
|
||||||
|
m.AddFields(
|
||||||
|
orm.Char("name", orm.FieldOpts{String: "Contract Reference", Required: true}),
|
||||||
|
orm.Many2one("employee_id", "hr.employee", orm.FieldOpts{String: "Employee", Required: true}),
|
||||||
|
orm.Many2one("department_id", "hr.department", orm.FieldOpts{String: "Department"}),
|
||||||
|
orm.Many2one("job_id", "hr.job", orm.FieldOpts{String: "Job Position"}),
|
||||||
|
orm.Date("date_start", orm.FieldOpts{String: "Start Date", Required: true}),
|
||||||
|
orm.Date("date_end", orm.FieldOpts{String: "End Date"}),
|
||||||
|
orm.Monetary("wage", orm.FieldOpts{String: "Wage", Required: true, CurrencyField: "currency_id"}),
|
||||||
|
orm.Many2one("currency_id", "res.currency", orm.FieldOpts{String: "Currency"}),
|
||||||
|
orm.Selection("state", []orm.SelectionItem{
|
||||||
|
{Value: "draft", Label: "New"},
|
||||||
|
{Value: "open", Label: "Running"},
|
||||||
|
{Value: "close", Label: "Expired"},
|
||||||
|
{Value: "cancel", Label: "Cancelled"},
|
||||||
|
}, orm.FieldOpts{String: "Status", Default: "draft"}),
|
||||||
|
orm.Many2one("company_id", "res.company", orm.FieldOpts{String: "Company"}),
|
||||||
|
orm.Text("notes", orm.FieldOpts{String: "Notes"}),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,4 +5,5 @@ func Init() {
|
|||||||
initHREmployee()
|
initHREmployee()
|
||||||
initHRDepartment()
|
initHRDepartment()
|
||||||
initHRJob()
|
initHRJob()
|
||||||
|
initHrContract()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,6 +126,16 @@ func initProjectTask() {
|
|||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// action_blocked: set kanban state to blocked
|
||||||
|
task.RegisterMethod("action_blocked", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
env := rs.Env()
|
||||||
|
for _, id := range rs.IDs() {
|
||||||
|
env.Tx().Exec(env.Ctx(),
|
||||||
|
`UPDATE project_task SET kanban_state = 'blocked' WHERE id = $1`, id)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
// Ensure fmt is used
|
// Ensure fmt is used
|
||||||
_ = fmt.Sprintf
|
_ = fmt.Sprintf
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,16 @@ func initPurchaseOrder() {
|
|||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// button_draft: Reset a cancelled PO back to draft (RFQ).
|
||||||
|
// Mirrors: odoo/addons/purchase/models/purchase_order.py PurchaseOrder.button_draft()
|
||||||
|
m.RegisterMethod("button_draft", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
env := rs.Env()
|
||||||
|
for _, poID := range rs.IDs() {
|
||||||
|
env.Tx().Exec(env.Ctx(), `UPDATE purchase_order SET state = 'draft' WHERE id = $1 AND state = 'cancel'`, poID)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
// action_create_bill: Generate a vendor bill (account.move in_invoice) from a confirmed PO.
|
// action_create_bill: Generate a vendor bill (account.move in_invoice) from a confirmed PO.
|
||||||
// Mirrors: odoo/addons/purchase/models/purchase_order.py PurchaseOrder.action_create_invoice()
|
// Mirrors: odoo/addons/purchase/models/purchase_order.py PurchaseOrder.action_create_invoice()
|
||||||
m.RegisterMethod("action_create_bill", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
m.RegisterMethod("action_create_bill", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
|||||||
@@ -584,6 +584,68 @@ func initSaleOrder() {
|
|||||||
}, nil
|
}, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// action_cancel: Cancel a sale order and linked draft invoices.
|
||||||
|
// Mirrors: odoo/addons/sale/models/sale_order.py SaleOrder.action_cancel()
|
||||||
|
m.RegisterMethod("action_cancel", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
env := rs.Env()
|
||||||
|
for _, soID := range rs.IDs() {
|
||||||
|
// Cancel linked draft invoices
|
||||||
|
rows, _ := env.Tx().Query(env.Ctx(),
|
||||||
|
`SELECT id FROM account_move WHERE invoice_origin = (SELECT name FROM sale_order WHERE id = $1) AND state = 'draft'`, soID)
|
||||||
|
for rows.Next() {
|
||||||
|
var invID int64
|
||||||
|
rows.Scan(&invID)
|
||||||
|
env.Tx().Exec(env.Ctx(), `UPDATE account_move SET state = 'cancel' WHERE id = $1`, invID)
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
env.Tx().Exec(env.Ctx(), `UPDATE sale_order SET state = 'cancel' WHERE id = $1`, soID)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// action_draft: Reset a cancelled sale order back to quotation.
|
||||||
|
// Mirrors: odoo/addons/sale/models/sale_order.py SaleOrder.action_draft()
|
||||||
|
m.RegisterMethod("action_draft", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
env := rs.Env()
|
||||||
|
for _, soID := range rs.IDs() {
|
||||||
|
env.Tx().Exec(env.Ctx(), `UPDATE sale_order SET state = 'draft' WHERE id = $1 AND state = 'cancel'`, soID)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// action_view_invoice: Open invoices linked to this sale order.
|
||||||
|
// Mirrors: odoo/addons/sale/models/sale_order.py SaleOrder.action_view_invoice()
|
||||||
|
m.RegisterMethod("action_view_invoice", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
env := rs.Env()
|
||||||
|
soID := rs.IDs()[0]
|
||||||
|
var soName string
|
||||||
|
env.Tx().QueryRow(env.Ctx(), `SELECT name FROM sale_order WHERE id = $1`, soID).Scan(&soName)
|
||||||
|
|
||||||
|
// Find invoices linked to this SO
|
||||||
|
rows, _ := env.Tx().Query(env.Ctx(),
|
||||||
|
`SELECT id FROM account_move WHERE invoice_origin = $1`, soName)
|
||||||
|
var invIDs []interface{}
|
||||||
|
for rows.Next() {
|
||||||
|
var id int64
|
||||||
|
rows.Scan(&id)
|
||||||
|
invIDs = append(invIDs, id)
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
|
||||||
|
if len(invIDs) == 1 {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"type": "ir.actions.act_window", "res_model": "account.move",
|
||||||
|
"res_id": invIDs[0], "view_mode": "form",
|
||||||
|
"views": [][]interface{}{{nil, "form"}}, "target": "current",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return map[string]interface{}{
|
||||||
|
"type": "ir.actions.act_window", "res_model": "account.move",
|
||||||
|
"view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}},
|
||||||
|
"domain": []interface{}{[]interface{}{"id", "in", invIDs}}, "target": "current",
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
|
||||||
// action_create_delivery: Generate a stock picking (delivery) from a confirmed sale order.
|
// action_create_delivery: Generate a stock picking (delivery) from a confirmed sale order.
|
||||||
// Mirrors: odoo/addons/sale/models/sale_order.py SaleOrder._action_confirm() → _create_picking()
|
// Mirrors: odoo/addons/sale/models/sale_order.py SaleOrder._action_confirm() → _create_picking()
|
||||||
m.RegisterMethod("action_create_delivery", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
m.RegisterMethod("action_create_delivery", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
|||||||
@@ -324,6 +324,17 @@ func initStockPicking() {
|
|||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// action_cancel: Cancel a picking and all its moves.
|
||||||
|
// Mirrors: odoo/addons/stock/models/stock_picking.py StockPicking.action_cancel()
|
||||||
|
m.RegisterMethod("action_cancel", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||||
|
env := rs.Env()
|
||||||
|
for _, pickingID := range rs.IDs() {
|
||||||
|
env.Tx().Exec(env.Ctx(), `UPDATE stock_move SET state = 'cancel' WHERE picking_id = $1`, pickingID)
|
||||||
|
env.Tx().Exec(env.Ctx(), `UPDATE stock_picking SET state = 'cancel' WHERE id = $1`, pickingID)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
// button_validate transitions a picking → done via _action_done on its moves.
|
// button_validate transitions a picking → done via _action_done on its moves.
|
||||||
// Properly updates quants and clears reservations.
|
// Properly updates quants and clears reservations.
|
||||||
// Mirrors: stock.picking.button_validate()
|
// Mirrors: stock.picking.button_validate()
|
||||||
|
|||||||
@@ -580,6 +580,8 @@ func seedViews(ctx context.Context, tx pgx.Tx) {
|
|||||||
<header>
|
<header>
|
||||||
<button name="action_confirm" string="Confirm" type="object" class="btn-primary" invisible="state != ''draft''"/>
|
<button name="action_confirm" string="Confirm" type="object" class="btn-primary" invisible="state != ''draft''"/>
|
||||||
<button name="create_invoices" string="Create Invoice" type="object" class="btn-primary" invisible="state != ''sale''"/>
|
<button name="create_invoices" string="Create Invoice" type="object" class="btn-primary" invisible="state != ''sale''"/>
|
||||||
|
<button name="action_cancel" string="Cancel" type="object" invisible="state not in (''draft'',''sale'')"/>
|
||||||
|
<button name="action_draft" string="Reset to Draft" type="object" invisible="state != ''cancel''"/>
|
||||||
<field name="state" widget="statusbar" clickable="1"/>
|
<field name="state" widget="statusbar" clickable="1"/>
|
||||||
</header>
|
</header>
|
||||||
<sheet>
|
<sheet>
|
||||||
@@ -622,6 +624,7 @@ func seedViews(ctx context.Context, tx pgx.Tx) {
|
|||||||
('invoice.form', 'account.move', 'form', '<form>
|
('invoice.form', 'account.move', 'form', '<form>
|
||||||
<header>
|
<header>
|
||||||
<button name="action_post" string="Post" type="object" class="btn-primary" invisible="state != ''draft''"/>
|
<button name="action_post" string="Post" type="object" class="btn-primary" invisible="state != ''draft''"/>
|
||||||
|
<button name="action_register_payment" string="Register Payment" type="object" class="btn-primary" invisible="state != ''posted'' or payment_state == ''paid''"/>
|
||||||
<button name="button_cancel" string="Cancel" type="object" invisible="state != ''draft''"/>
|
<button name="button_cancel" string="Cancel" type="object" invisible="state != ''draft''"/>
|
||||||
<button name="button_draft" string="Reset to Draft" type="object" invisible="state != ''cancel''"/>
|
<button name="button_draft" string="Reset to Draft" type="object" invisible="state != ''cancel''"/>
|
||||||
<field name="state" widget="statusbar" clickable="1"/>
|
<field name="state" widget="statusbar" clickable="1"/>
|
||||||
@@ -1263,6 +1266,14 @@ func seedDemoData(ctx context.Context, tx pgx.Tx) {
|
|||||||
(3, 'Peter Weber', 3, 1, 'peter@bauer-bau.de')
|
(3, 'Peter Weber', 3, 1, 'peter@bauer-bau.de')
|
||||||
ON CONFLICT (id) DO NOTHING`)
|
ON CONFLICT (id) DO NOTHING`)
|
||||||
|
|
||||||
|
// Project task stages
|
||||||
|
tx.Exec(ctx, `INSERT INTO project_task_type (id, name, sequence, fold) VALUES
|
||||||
|
(1, 'New', 1, false),
|
||||||
|
(2, 'In Progress', 5, false),
|
||||||
|
(3, 'Done', 10, true),
|
||||||
|
(4, 'Cancelled', 20, true)
|
||||||
|
ON CONFLICT (id) DO NOTHING`)
|
||||||
|
|
||||||
// Projects
|
// Projects
|
||||||
tx.Exec(ctx, `INSERT INTO project_project (id, name, partner_id, company_id, active) VALUES
|
tx.Exec(ctx, `INSERT INTO project_project (id, name, partner_id, company_id, active) VALUES
|
||||||
(1, 'Website Redesign', 5, 1, true),
|
(1, 'Website Redesign', 5, 1, true),
|
||||||
|
|||||||
Reference in New Issue
Block a user