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

@@ -88,7 +88,7 @@ func TestExtractImports(t *testing.T) {
content := `import { Foo, Bar } from "@web/core/foo";
import { Baz as Qux } from "@web/core/baz";
const x = 1;`
deps, requires, clean := extractImports(content)
deps, requires, clean := extractImports("test.module", content)
if len(deps) != 2 {
t.Fatalf("expected 2 deps, got %d: %v", len(deps), deps)
@@ -120,7 +120,7 @@ const x = 1;`
t.Run("default import", func(t *testing.T) {
content := `import Foo from "@web/core/foo";`
deps, requires, _ := extractImports(content)
deps, requires, _ := extractImports("test.module", content)
if len(deps) != 1 || deps[0] != "@web/core/foo" {
t.Errorf("deps = %v, want [@web/core/foo]", deps)
@@ -132,7 +132,7 @@ const x = 1;`
t.Run("namespace import", func(t *testing.T) {
content := `import * as utils from "@web/core/utils";`
deps, requires, _ := extractImports(content)
deps, requires, _ := extractImports("test.module", content)
if len(deps) != 1 || deps[0] != "@web/core/utils" {
t.Errorf("deps = %v, want [@web/core/utils]", deps)
@@ -144,7 +144,7 @@ const x = 1;`
t.Run("side-effect import", func(t *testing.T) {
content := `import "@web/core/setup";`
deps, requires, _ := extractImports(content)
deps, requires, _ := extractImports("test.module", content)
if len(deps) != 1 || deps[0] != "@web/core/setup" {
t.Errorf("deps = %v, want [@web/core/setup]", deps)
@@ -157,7 +157,7 @@ const x = 1;`
t.Run("dedup deps", func(t *testing.T) {
content := `import { Foo } from "@web/core/foo";
import { Bar } from "@web/core/foo";`
deps, _, _ := extractImports(content)
deps, _, _ := extractImports("test.module", content)
if len(deps) != 1 {
t.Errorf("expected deduped deps, got %v", deps)
@@ -167,7 +167,7 @@ import { Bar } from "@web/core/foo";`
func TestTransformExports(t *testing.T) {
t.Run("export class", func(t *testing.T) {
got := transformExports("export class Foo extends Bar {")
got, _ := transformExports("export class Foo extends Bar {")
want := "const Foo = __exports.Foo = class Foo extends Bar {"
if got != want {
t.Errorf("got %q, want %q", got, want)
@@ -175,15 +175,18 @@ func TestTransformExports(t *testing.T) {
})
t.Run("export function", func(t *testing.T) {
got := transformExports("export function doSomething(a, b) {")
want := `__exports.doSomething = function doSomething(a, b) {`
got, deferred := transformExports("export function doSomething(a, b) {")
want := `function doSomething(a, b) {`
if got != want {
t.Errorf("got %q, want %q", got, want)
}
if len(deferred) != 1 || deferred[0] != "doSomething" {
t.Errorf("deferred = %v, want [doSomething]", deferred)
}
})
t.Run("export const", func(t *testing.T) {
got := transformExports("export const MAX_SIZE = 100;")
got, _ := transformExports("export const MAX_SIZE = 100;")
want := "const MAX_SIZE = __exports.MAX_SIZE = 100;"
if got != want {
t.Errorf("got %q, want %q", got, want)
@@ -191,7 +194,7 @@ func TestTransformExports(t *testing.T) {
})
t.Run("export let", func(t *testing.T) {
got := transformExports("export let counter = 0;")
got, _ := transformExports("export let counter = 0;")
want := "let counter = __exports.counter = 0;"
if got != want {
t.Errorf("got %q, want %q", got, want)
@@ -199,7 +202,7 @@ func TestTransformExports(t *testing.T) {
})
t.Run("export default", func(t *testing.T) {
got := transformExports("export default Foo;")
got, _ := transformExports("export default Foo;")
want := `__exports[Symbol.for("default")] = Foo;`
if got != want {
t.Errorf("got %q, want %q", got, want)
@@ -207,7 +210,7 @@ func TestTransformExports(t *testing.T) {
})
t.Run("export named", func(t *testing.T) {
got := transformExports("export { Foo, Bar };")
got, _ := transformExports("export { Foo, Bar };")
if !strings.Contains(got, "__exports.Foo = Foo;") {
t.Errorf("missing Foo export in: %s", got)
}
@@ -217,7 +220,7 @@ func TestTransformExports(t *testing.T) {
})
t.Run("export named with alias", func(t *testing.T) {
got := transformExports("export { Foo as default };")
got, _ := transformExports("export { Foo as default };")
if !strings.Contains(got, "__exports.default = Foo;") {
t.Errorf("missing aliased export in: %s", got)
}