feat: Portal, Email Inbound, Discuss + module improvements

- Portal: /my/* routes, signup, password reset, portal user support
- Email Inbound: IMAP polling (go-imap/v2), thread matching
- Discuss: mail.channel, long-polling bus, DM, unread count
- Cron: ir.cron runner (goroutine scheduler)
- Bank Import, CSV/Excel Import
- Automation (ir.actions.server)
- Fetchmail service
- HR Payroll model
- Various fixes across account, sale, stock, purchase, crm, hr, project

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marc
2026-04-12 18:41:57 +02:00
parent 2c7c1e6c88
commit 66383adf06
87 changed files with 14696 additions and 654 deletions

View File

@@ -127,13 +127,22 @@ func initStockValuationLayer() {
}
defer rows.Close()
var totalConsumedValue float64
// Collect layers first, then close cursor before updating (pgx safety)
type layerConsumption struct {
id int64
newQty float64
newValue float64
consumed float64
cost float64
}
var consumptions []layerConsumption
remaining := qtyToConsume
for rows.Next() && remaining > 0 {
var layerID int64
var layerQty, layerValue, layerUnitCost float64
if err := rows.Scan(&layerID, &layerQty, &layerValue, &layerUnitCost); err != nil {
rows.Close()
return nil, fmt.Errorf("stock.valuation.layer: scan FIFO layer: %w", err)
}
@@ -142,20 +151,27 @@ func initStockValuationLayer() {
consumed = layerQty
}
consumedValue := consumed * layerUnitCost
newRemainingQty := layerQty - consumed
newRemainingValue := layerValue - consumedValue
consumptions = append(consumptions, layerConsumption{
id: layerID,
newQty: layerQty - consumed,
newValue: layerValue - consumed*layerUnitCost,
consumed: consumed,
cost: layerUnitCost,
})
remaining -= consumed
}
rows.Close()
// Now update layers outside the cursor
var totalConsumedValue float64
for _, c := range consumptions {
_, err := env.Tx().Exec(env.Ctx(),
`UPDATE stock_valuation_layer SET remaining_qty = $1, remaining_value = $2 WHERE id = $3`,
newRemainingQty, newRemainingValue, layerID,
)
c.newQty, c.newValue, c.id)
if err != nil {
return nil, fmt.Errorf("stock.valuation.layer: update layer %d: %w", layerID, err)
return nil, fmt.Errorf("stock.valuation.layer: update layer %d: %w", c.id, err)
}
totalConsumedValue += consumedValue
remaining -= consumed
totalConsumedValue += c.consumed * c.cost
}
return map[string]interface{}{