fixed ledger account listing + quotation listing #309

Merged
tech merged 1 commits from quote-308 into main 2026-01-23 01:48:55 +00:00
60 changed files with 469 additions and 283 deletions

View File

@@ -49,6 +49,6 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
) )

View File

@@ -212,8 +212,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -45,7 +45,7 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
google.golang.org/grpc v1.78.0 // indirect google.golang.org/grpc v1.78.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect google.golang.org/protobuf v1.36.11 // indirect
) )

View File

@@ -212,8 +212,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -49,7 +49,7 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
google.golang.org/grpc v1.78.0 // indirect google.golang.org/grpc v1.78.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect google.golang.org/protobuf v1.36.11 // indirect
) )

View File

@@ -212,8 +212,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -50,5 +50,5 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
) )

View File

@@ -212,8 +212,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -86,5 +86,5 @@ require (
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
golang.org/x/time v0.14.0 // indirect golang.org/x/time v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
) )

View File

@@ -362,8 +362,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -50,5 +50,5 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
) )

View File

@@ -214,8 +214,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -47,5 +47,5 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
) )

View File

@@ -212,8 +212,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -51,5 +51,5 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
) )

View File

@@ -214,8 +214,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -52,7 +52,7 @@ require (
golang.org/x/net v0.49.0 // indirect golang.org/x/net v0.49.0 // indirect
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
google.golang.org/grpc v1.78.0 // indirect google.golang.org/grpc v1.78.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect google.golang.org/protobuf v1.36.11 // indirect
) )

View File

@@ -229,8 +229,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -62,5 +62,5 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
) )

View File

@@ -215,8 +215,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -48,7 +48,7 @@ func NewQuotes(logger mlogger.Logger, repo repository.Repository, retention time
}, },
Unique: true, Unique: true,
Name: "payment_quotes_org_idempotency_key", Name: "payment_quotes_org_idempotency_key",
PartialFilter: repository.Query().Comparison(repository.Field("idempotencyKey"), builder.Ne, ""), PartialFilter: repository.Query().Comparison(repository.Field("idempotencyKey"), builder.Exists, true),
}, },
{ {
Keys: []ri.Key{{Field: "organizationRef", Sort: ri.Asc}}, Keys: []ri.Key{{Field: "organizationRef", Sort: ri.Asc}},

View File

@@ -93,6 +93,6 @@ require (
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View File

@@ -269,8 +269,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda h1:+2XxjfsAu6vqFxwGBRcHiMaDCuZiqXGDUDVWVtrFAnE= google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda h1:+2XxjfsAu6vqFxwGBRcHiMaDCuZiqXGDUDVWVtrFAnE=
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -139,6 +139,6 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
google.golang.org/grpc v1.78.0 // indirect google.golang.org/grpc v1.78.0 // indirect
) )

View File

@@ -361,8 +361,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda h1:+2XxjfsAu6vqFxwGBRcHiMaDCuZiqXGDUDVWVtrFAnE= google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda h1:+2XxjfsAu6vqFxwGBRcHiMaDCuZiqXGDUDVWVtrFAnE=
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View File

@@ -6,23 +6,25 @@ import (
"github.com/tech/sendico/pkg/api/http/response" "github.com/tech/sendico/pkg/api/http/response"
"github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1" ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
) )
type ledgerAccount struct { type ledgerAccount struct {
LedgerAccountRef string `json:"ledgerAccountRef"` model.Describable `bson:",inline" json:",inline"`
OrganizationRef string `json:"organizationRef"` LedgerAccountRef string `json:"ledgerAccountRef"`
OwnerRef string `json:"ownerRef,omitempty"` OrganizationRef string `json:"organizationRef"`
AccountCode string `json:"accountCode"` OwnerRef string `json:"ownerRef,omitempty"`
AccountType string `json:"accountType"` AccountCode string `json:"accountCode"`
Currency string `json:"currency"` AccountType string `json:"accountType"`
Status string `json:"status"` Currency string `json:"currency"`
AllowNegative bool `json:"allowNegative"` Status string `json:"status"`
IsSettlement bool `json:"isSettlement"` AllowNegative bool `json:"allowNegative"`
Metadata map[string]string `json:"metadata,omitempty"` IsSettlement bool `json:"isSettlement"`
CreatedAt time.Time `json:"createdAt,omitempty"` Metadata map[string]string `json:"metadata,omitempty"`
UpdatedAt time.Time `json:"updatedAt,omitempty"` CreatedAt time.Time `json:"createdAt,omitempty"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
} }
type ledgerAccountsResponse struct { type ledgerAccountsResponse struct {
@@ -82,6 +84,10 @@ func toLedgerAccount(acc *ledgerv1.LedgerAccount) ledgerAccount {
return ledgerAccount{} return ledgerAccount{}
} }
return ledgerAccount{ return ledgerAccount{
Describable: model.Describable{
Name: acc.GetDescribable().GetName(),
Description: acc.GetDescribable().Description,
},
LedgerAccountRef: acc.GetLedgerAccountRef(), LedgerAccountRef: acc.GetLedgerAccountRef(),
OrganizationRef: acc.GetOrganizationRef(), OrganizationRef: acc.GetOrganizationRef(),
OwnerRef: acc.GetOwnerRef(), OwnerRef: acc.GetOwnerRef(),

View File

@@ -0,0 +1,46 @@
import 'package:flutter/foundation.dart';
import 'package:pshared/provider/ledger.dart';
class LedgerBalanceMaskController with ChangeNotifier {
String? _orgRef;
final Set<String> _maskedBalanceRefs = <String>{};
Set<String> _knownAccountRefs = <String>{};
void update(LedgerAccountsProvider accountsProvider) {
final nextOrgRef = accountsProvider.organizationRef;
final orgChanged = nextOrgRef != _orgRef;
if (orgChanged) {
_orgRef = nextOrgRef;
_maskedBalanceRefs.clear();
_knownAccountRefs = <String>{};
}
final refs = accountsProvider.accounts.map((a) => a.ledgerAccountRef).toSet();
_maskedBalanceRefs.removeWhere((id) => !refs.contains(id));
final newRefs = refs.difference(_knownAccountRefs);
if (newRefs.isNotEmpty) {
_maskedBalanceRefs.addAll(newRefs);
}
_knownAccountRefs = refs;
notifyListeners();
}
bool isBalanceMasked(String ledgerAccountRef) => _maskedBalanceRefs.contains(ledgerAccountRef);
bool isBalanceVisible(String ledgerAccountRef) => !isBalanceMasked(ledgerAccountRef);
void toggleBalanceMask(String ledgerAccountRef) {
final existed = _maskedBalanceRefs.remove(ledgerAccountRef);
if (!existed) _maskedBalanceRefs.add(ledgerAccountRef);
notifyListeners();
}
void unmaskAllBalances() {
_maskedBalanceRefs.clear();
notifyListeners();
}
}

View File

@@ -0,0 +1,106 @@
import 'package:flutter/foundation.dart';
import 'package:collection/collection.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pshared/provider/payment/wallets.dart';
class WalletsController with ChangeNotifier {
late WalletsProvider _wallets;
String? _orgRef;
/// UI-only: wallet refs with masked balances
final Set<String> _maskedBalanceWalletRefs = <String>{};
Set<String> _knownWalletRefs = <String>{};
String? _selectedWalletRef;
bool get isLoading => _wallets.isLoading;
Exception? get error => _wallets.error;
void update(WalletsProvider wallets) {
_wallets = wallets;
final nextOrgRef = wallets.organizationRef;
final orgChanged = nextOrgRef != _orgRef;
if (orgChanged) {
_orgRef = nextOrgRef;
_maskedBalanceWalletRefs.clear();
_knownWalletRefs = <String>{};
_selectedWalletRef = null;
}
// Remove ids that no longer exist
final ids = wallets.wallets.map((w) => w.id).toSet();
_maskedBalanceWalletRefs.removeWhere((id) => !ids.contains(id));
final newIds = ids.difference(_knownWalletRefs);
if (newIds.isNotEmpty) {
_maskedBalanceWalletRefs.addAll(newIds);
}
_knownWalletRefs = ids;
_selectedWalletRef = _resolveSelectedId(
currentRef: _selectedWalletRef,
wallets: wallets.wallets,
);
notifyListeners();
}
List<Wallet> get wallets => _wallets.wallets;
bool isBalanceMasked(String walletRef) => _maskedBalanceWalletRefs.contains(walletRef);
bool isBalanceVisible(String walletRef) => !isBalanceMasked(walletRef);
List<Wallet> get unmaskedWallets =>
wallets.where((w) => !_maskedBalanceWalletRefs.contains(w.id)).toList(growable: false);
Wallet? get selectedWallet {
final id = _selectedWalletRef;
if (id == null) return null;
return wallets.firstWhereOrNull((w) => w.id == id);
}
String? get selectedWalletRef => _selectedWalletRef;
void selectWallet(Wallet wallet) => selectWalletByRef(wallet.id);
void selectWalletByRef(String walletRef) {
if (_selectedWalletRef == walletRef) return;
_selectedWalletRef = walletRef;
notifyListeners();
}
/// Toggle wallet balance masking
void toggleBalanceMask(String walletRef) {
final existed = _maskedBalanceWalletRefs.remove(walletRef);
if (!existed) _maskedBalanceWalletRefs.add(walletRef);
notifyListeners();
}
/// Unmask balances for all wallets (bulk action)
void unmaskAllBalances() {
_maskedBalanceWalletRefs.clear();
notifyListeners();
}
String? _resolveSelectedId({
required String? currentRef,
required List<Wallet> wallets,
}) {
if (wallets.isEmpty) return null;
// Keep current selection if it still exists
if (currentRef != null && wallets.any((w) => w.id == currentRef)) {
return currentRef;
}
// Fallback to the first wallet
return wallets.first.id;
}
}

View File

@@ -1,124 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:collection/collection.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pshared/provider/payment/wallets.dart';
class WalletsController with ChangeNotifier {
late WalletsProvider _wallets;
String? _orgRef;
/// UI-only: which wallets are allowed to be visible
final Set<String> _visibleWalletRefs = <String>{};
String? _selectedWalletRef;
bool get isLoading => _wallets.isLoading;
Exception? get error => _wallets.error;
void update(WalletsProvider wallets) {
_wallets = wallets;
final nextOrgRef = wallets.organizationRef;
final orgChanged = nextOrgRef != _orgRef;
if (orgChanged) {
_orgRef = nextOrgRef;
_visibleWalletRefs.clear(); // All wallets hidden on org change
_selectedWalletRef = null;
}
// Remove ids that no longer exist
final ids = wallets.wallets.map((w) => w.id).toSet();
_visibleWalletRefs.removeWhere((id) => !ids.contains(id));
_selectedWalletRef = _resolveSelectedId(
currentRef: _selectedWalletRef,
wallets: wallets.wallets,
visibleRefs: _visibleWalletRefs,
);
notifyListeners();
}
List<Wallet> get wallets => _wallets.wallets;
bool isVisible(String walletRef) => _visibleWalletRefs.contains(walletRef);
bool isHidden(String walletRef) => !isVisible(walletRef);
List<Wallet> get visibleWallets =>
wallets.where((w) => _visibleWalletRefs.contains(w.id)).toList(growable: false);
Wallet? get selectedWallet {
final id = _selectedWalletRef;
if (id == null) return null;
return wallets.firstWhereOrNull((w) => w.id == id);
}
String? get selectedWalletRef => _selectedWalletRef;
void selectWallet(Wallet wallet, {bool allowHidden = false}) =>
selectWalletByRef(wallet.id, allowHidden: allowHidden);
void selectWalletByRef(String walletRef, {bool allowHidden = false}) {
if (_selectedWalletRef == walletRef) return;
// Prevent selecting a hidden wallet
if (!allowHidden && !_visibleWalletRefs.contains(walletRef)) return;
_selectedWalletRef = walletRef;
notifyListeners();
}
/// Toggle wallet visibility
void toggleVisibility(String accountRef) {
final existed = _visibleWalletRefs.remove(accountRef);
if (!existed) _visibleWalletRefs.add(accountRef);
_selectedWalletRef = _resolveSelectedId(
currentRef: _selectedWalletRef,
wallets: wallets,
visibleRefs: _visibleWalletRefs,
);
notifyListeners();
}
/// Show all wallets (bulk action)
void showAll() {
final allRefs = wallets.map((w) => w.id);
_visibleWalletRefs
..clear()
..addAll(allRefs);
_selectedWalletRef = _resolveSelectedId(
currentRef: _selectedWalletRef,
wallets: wallets,
visibleRefs: _visibleWalletRefs,
);
notifyListeners();
}
String? _resolveSelectedId({
required String? currentRef,
required List<Wallet> wallets,
required Set<String> visibleRefs,
}) {
if (wallets.isEmpty) return null;
// Keep current selection if it still exists and is visible
if (currentRef != null &&
visibleRefs.contains(currentRef) &&
wallets.any((w) => w.id == currentRef)) {
return currentRef;
}
// Select the first visible wallet
final firstVisible = wallets.firstWhereOrNull((w) => visibleRefs.contains(w.id));
return firstVisible?.id;
}
}

View File

@@ -14,8 +14,10 @@ class LedgerAccountDTO {
final String organizationRef; final String organizationRef;
final String? ownerRef; final String? ownerRef;
final String accountCode; final String accountCode;
@JsonKey(fromJson: ledgerAccountTypeFromJson, toJson: ledgerAccountTypeToJson)
final LedgerAccountTypeDTO accountType; final LedgerAccountTypeDTO accountType;
final String currency; final String currency;
@JsonKey(fromJson: ledgerAccountStatusFromJson, toJson: ledgerAccountStatusToJson)
final LedgerAccountStatusDTO status; final LedgerAccountStatusDTO status;
final bool allowNegative; final bool allowNegative;
final bool isSettlement; final bool isSettlement;
@@ -23,7 +25,7 @@ class LedgerAccountDTO {
final DateTime? createdAt; final DateTime? createdAt;
final DateTime? updatedAt; final DateTime? updatedAt;
final DescribableDTO describable; final DescribableDTO? describable;
final LedgerBalanceDTO? balance; final LedgerBalanceDTO? balance;
@@ -40,7 +42,7 @@ class LedgerAccountDTO {
this.metadata, this.metadata,
this.createdAt, this.createdAt,
this.updatedAt, this.updatedAt,
required this.describable, this.describable,
this.balance, this.balance,
}); });

View File

@@ -11,3 +11,34 @@ enum LedgerAccountStatusDTO {
@JsonValue('frozen') @JsonValue('frozen')
frozen, frozen,
} }
LedgerAccountStatusDTO ledgerAccountStatusFromJson(Object? value) {
final raw = value?.toString() ?? '';
var normalized = raw.trim().toLowerCase();
const prefix = 'account_status_';
if (normalized.startsWith(prefix)) {
normalized = normalized.substring(prefix.length);
}
switch (normalized) {
case 'active':
return LedgerAccountStatusDTO.active;
case 'frozen':
return LedgerAccountStatusDTO.frozen;
case 'unspecified':
case '':
return LedgerAccountStatusDTO.unspecified;
default:
return LedgerAccountStatusDTO.unspecified;
}
}
String ledgerAccountStatusToJson(LedgerAccountStatusDTO value) {
switch (value) {
case LedgerAccountStatusDTO.active:
return 'active';
case LedgerAccountStatusDTO.frozen:
return 'frozen';
case LedgerAccountStatusDTO.unspecified:
return 'unspecified';
}
}

View File

@@ -17,3 +17,42 @@ enum LedgerAccountTypeDTO {
@JsonValue('expense') @JsonValue('expense')
expense, expense,
} }
LedgerAccountTypeDTO ledgerAccountTypeFromJson(Object? value) {
final raw = value?.toString() ?? '';
var normalized = raw.trim().toLowerCase();
const prefix = 'account_type_';
if (normalized.startsWith(prefix)) {
normalized = normalized.substring(prefix.length);
}
switch (normalized) {
case 'asset':
return LedgerAccountTypeDTO.asset;
case 'liability':
return LedgerAccountTypeDTO.liability;
case 'revenue':
return LedgerAccountTypeDTO.revenue;
case 'expense':
return LedgerAccountTypeDTO.expense;
case 'unspecified':
case '':
return LedgerAccountTypeDTO.unspecified;
default:
return LedgerAccountTypeDTO.unspecified;
}
}
String ledgerAccountTypeToJson(LedgerAccountTypeDTO value) {
switch (value) {
case LedgerAccountTypeDTO.asset:
return 'asset';
case LedgerAccountTypeDTO.liability:
return 'liability';
case LedgerAccountTypeDTO.revenue:
return 'revenue';
case LedgerAccountTypeDTO.expense:
return 'expense';
case LedgerAccountTypeDTO.unspecified:
return 'unspecified';
}
}

View File

@@ -3,6 +3,7 @@ import 'package:pshared/data/mapper/describable.dart';
import 'package:pshared/data/mapper/ledger/balance.dart'; import 'package:pshared/data/mapper/ledger/balance.dart';
import 'package:pshared/data/mapper/ledger/status.dart'; import 'package:pshared/data/mapper/ledger/status.dart';
import 'package:pshared/data/mapper/ledger/type.dart'; import 'package:pshared/data/mapper/ledger/type.dart';
import 'package:pshared/models/describable.dart';
import 'package:pshared/models/ledger/account.dart'; import 'package:pshared/models/ledger/account.dart';
@@ -20,7 +21,7 @@ extension LedgerAccountDTOMapper on LedgerAccountDTO {
metadata: metadata, metadata: metadata,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
describable: describable.toDomain(), describable: describable?.toDomain() ?? newDescribable(name: '', description: null),
balance: balance?.toDomain(), balance: balance?.toDomain(),
); );
} }

View File

@@ -24,7 +24,7 @@ class LedgerAccountsProvider with ChangeNotifier {
Resource<List<LedgerAccount>> _resource = Resource(data: []); Resource<List<LedgerAccount>> _resource = Resource(data: []);
Resource<List<LedgerAccount>> get resource => _resource; Resource<List<LedgerAccount>> get resource => _resource;
List<LedgerAccount> get accounts => _resource.data ?? []; List<LedgerAccount> get accounts => (_resource.data ?? []).whereNot((la)=> la.isSettlement).toList();
bool get isLoading => _resource.isLoading; bool get isLoading => _resource.isLoading;
Exception? get error => _resource.error; Exception? get error => _resource.error;

View File

@@ -1,12 +1,14 @@
import 'package:collection/collection.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/controllers/wallets.dart';
import 'package:pshared/models/payment/currency_pair.dart'; import 'package:pshared/models/payment/currency_pair.dart';
import 'package:pshared/models/payment/customer.dart'; import 'package:pshared/models/payment/customer.dart';
import 'package:pshared/models/payment/fx/intent.dart'; import 'package:pshared/models/payment/fx/intent.dart';
import 'package:pshared/models/payment/fx/side.dart'; import 'package:pshared/models/payment/fx/side.dart';
import 'package:pshared/models/payment/kind.dart'; import 'package:pshared/models/payment/kind.dart';
import 'package:pshared/models/payment/methods/card.dart';
import 'package:pshared/models/payment/methods/data.dart';
import 'package:pshared/models/payment/methods/iban.dart';
import 'package:pshared/models/payment/methods/managed_wallet.dart'; import 'package:pshared/models/payment/methods/managed_wallet.dart';
import 'package:pshared/models/payment/methods/russian_bank.dart';
import 'package:pshared/models/payment/methods/type.dart'; import 'package:pshared/models/payment/methods/type.dart';
import 'package:pshared/models/money.dart'; import 'package:pshared/models/money.dart';
import 'package:pshared/models/payment/settlement_mode.dart'; import 'package:pshared/models/payment/settlement_mode.dart';
@@ -15,7 +17,6 @@ import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/payment/amount.dart'; import 'package:pshared/provider/payment/amount.dart';
import 'package:pshared/provider/payment/flow.dart'; import 'package:pshared/provider/payment/flow.dart';
import 'package:pshared/provider/recipient/provider.dart'; import 'package:pshared/provider/recipient/provider.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pshared/utils/currency.dart'; import 'package:pshared/utils/currency.dart';
@@ -25,15 +26,16 @@ class QuotationIntentBuilder {
required WalletsController wallets, required WalletsController wallets,
required PaymentFlowProvider flow, required PaymentFlowProvider flow,
required RecipientsProvider recipients, required RecipientsProvider recipients,
required PaymentMethodsProvider methods,
}) { }) {
final selectedWallet = wallets.selectedWallet; final selectedWallet = wallets.selectedWallet;
final method = methods.methods.firstWhereOrNull((m) => m.type == flow.selectedType); final paymentData = flow.selectedPaymentData;
if (selectedWallet == null || method == null) return null; final selectedMethod = flow.selectedMethod;
if (selectedWallet == null || paymentData == null) return null;
final customer = _buildCustomer( final customer = _buildCustomer(
recipient: recipients.currentObject, recipient: recipients.currentObject,
method: method, method: selectedMethod,
data: paymentData,
); );
final amount = Money( final amount = Money(
amount: payment.amount.toString(), amount: payment.amount.toString(),
@@ -50,7 +52,7 @@ class QuotationIntentBuilder {
return PaymentIntent( return PaymentIntent(
kind: PaymentKind.payout, kind: PaymentKind.payout,
amount: amount, amount: amount,
destination: method.data, destination: paymentData,
source: ManagedWalletPaymentMethod( source: ManagedWalletPaymentMethod(
managedWalletRef: selectedWallet.id, managedWalletRef: selectedWallet.id,
), ),
@@ -85,11 +87,19 @@ class QuotationIntentBuilder {
return amount.currency; return amount.currency;
} }
Customer _buildCustomer({ Customer? _buildCustomer({
required Recipient? recipient, required Recipient? recipient,
required PaymentMethod method, required PaymentMethod? method,
required PaymentMethodData? data,
}) { }) {
final name = _resolveCustomerName(method, recipient); final id = recipient?.id ?? method?.recipientRef;
if (id == null || id.isEmpty) return null;
final name = _resolveCustomerName(
method: method,
data: data,
recipient: recipient,
);
final parts = name == null || name.trim().isEmpty final parts = name == null || name.trim().isEmpty
? const <String>[] ? const <String>[]
: name.trim().split(RegExp(r'\s+')); : name.trim().split(RegExp(r'\s+'));
@@ -99,26 +109,31 @@ class QuotationIntentBuilder {
parts.length > 2 ? parts.sublist(1, parts.length - 1).join(' ') : null; parts.length > 2 ? parts.sublist(1, parts.length - 1).join(' ') : null;
return Customer( return Customer(
id: recipient?.id ?? method.recipientRef, id: id,
firstName: firstName, firstName: firstName,
middleName: middleName, middleName: middleName,
lastName: lastName, lastName: lastName,
country: method.cardData?.country, country: _resolveCustomerCountry(method: method, data: data),
); );
} }
String? _resolveCustomerName(PaymentMethod method, Recipient? recipient) { String? _resolveCustomerName({
final card = method.cardData; required PaymentMethod? method,
required PaymentMethodData? data,
required Recipient? recipient,
}) {
final card = method?.cardData ?? (data is CardPaymentMethod ? data : null);
if (card != null) { if (card != null) {
return '${card.firstName} ${card.lastName}'.trim(); final fullName = '${card.firstName} ${card.lastName}'.trim();
if (fullName.isNotEmpty) return fullName;
} }
final iban = method.ibanData; final iban = method?.ibanData ?? (data is IbanPaymentMethod ? data : null);
if (iban != null && iban.accountHolder.trim().isNotEmpty) { if (iban != null && iban.accountHolder.trim().isNotEmpty) {
return iban.accountHolder.trim(); return iban.accountHolder.trim();
} }
final bank = method.bankAccountData; final bank = method?.bankAccountData ?? (data is RussianBankAccountPaymentMethod ? data : null);
if (bank != null && bank.recipientName.trim().isNotEmpty) { if (bank != null && bank.recipientName.trim().isNotEmpty) {
return bank.recipientName.trim(); return bank.recipientName.trim();
} }
@@ -126,4 +141,12 @@ class QuotationIntentBuilder {
final recipientName = recipient?.name.trim(); final recipientName = recipient?.name.trim();
return recipientName?.isNotEmpty == true ? recipientName : null; return recipientName?.isNotEmpty == true ? recipientName : null;
} }
String? _resolveCustomerCountry({
required PaymentMethod? method,
required PaymentMethodData? data,
}) {
final card = method?.cardData ?? (data is CardPaymentMethod ? data : null);
return card?.country;
}
} }

View File

@@ -7,7 +7,7 @@ import 'package:logging/logging.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import 'package:pshared/api/requests/payment/quote.dart'; import 'package:pshared/api/requests/payment/quote.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/data/mapper/payment/intent/payment.dart'; import 'package:pshared/data/mapper/payment/intent/payment.dart';
import 'package:pshared/models/asset.dart'; import 'package:pshared/models/asset.dart';
import 'package:pshared/models/payment/intent.dart'; import 'package:pshared/models/payment/intent.dart';
@@ -38,7 +38,7 @@ class QuotationProvider extends ChangeNotifier {
WalletsController wallets, WalletsController wallets,
PaymentFlowProvider flow, PaymentFlowProvider flow,
RecipientsProvider recipients, RecipientsProvider recipients,
PaymentMethodsProvider methods, PaymentMethodsProvider _methods,
) { ) {
_organizations = venue; _organizations = venue;
final intent = _intentBuilder.build( final intent = _intentBuilder.build(
@@ -46,7 +46,6 @@ class QuotationProvider extends ChangeNotifier {
wallets: wallets, wallets: wallets,
flow: flow, flow: flow,
recipients: recipients, recipients: recipients,
methods: methods,
); );
if (intent == null) return; if (intent == null) return;
final intentKey = _buildIntentKey(intent); final intentKey = _buildIntentKey(intent);

View File

@@ -4,7 +4,7 @@ import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/models/payment/type.dart'; import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/recipient/recipient.dart'; import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/organizations.dart'; import 'package:pshared/provider/organizations.dart';
@@ -279,7 +279,7 @@ void _openWalletEdit(
Wallet wallet, { Wallet wallet, {
required PayoutDestination returnTo, required PayoutDestination returnTo,
}) { }) {
context.read<WalletsController>().selectWallet(wallet, allowHidden: true); context.read<WalletsController>().selectWallet(wallet);
context.pushToEditWallet(returnTo: returnTo); context.pushToEditWallet(returnTo: returnTo);
} }
@@ -288,7 +288,7 @@ void _openWalletTopUp(
Wallet wallet, { Wallet wallet, {
required PayoutDestination returnTo, required PayoutDestination returnTo,
}) { }) {
context.read<WalletsController>().selectWallet(wallet, allowHidden: true); context.read<WalletsController>().selectWallet(wallet);
context.pushToWalletTopUp(returnTo: returnTo); context.pushToWalletTopUp(returnTo: returnTo);
} }

View File

@@ -116,6 +116,14 @@
"@colDataOwner": { "@colDataOwner": {
"description": "Column header for who manages the payout data" "description": "Column header for who manages the payout data"
}, },
"assetOwner": "Owner",
"@assetOwner": {
"description": "Label for selecting the owner of a wallet or ledger account"
},
"organizationOwned": "Organization-owned",
"@organizationOwned": {
"description": "Option label for assets owned by the organization"
},
"colAvatar": "Avatar", "colAvatar": "Avatar",
"@colAvatar": { "@colAvatar": {

View File

@@ -116,6 +116,14 @@
"@colDataOwner": { "@colDataOwner": {
"description": "Заголовок столбца для указания, кто управляет данными о выплатах" "description": "Заголовок столбца для указания, кто управляет данными о выплатах"
}, },
"assetOwner": "Владелец",
"@assetOwner": {
"description": "Метка выбора владельца кошелька или счета"
},
"organizationOwned": "Организация",
"@organizationOwned": {
"description": "Опция для активов, принадлежащих организации"
},
"colAvatar": "Аватар", "colAvatar": "Аватар",
"@colAvatar": { "@colAvatar": {

View File

@@ -8,7 +8,8 @@ import 'package:provider/provider.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:pshared/config/constants.dart'; import 'package:pshared/config/constants.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/ledger_accounts.dart';
import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/provider/locale.dart'; import 'package:pshared/provider/locale.dart';
import 'package:pshared/provider/permissions.dart'; import 'package:pshared/provider/permissions.dart';
import 'package:pshared/provider/account.dart'; import 'package:pshared/provider/account.dart';
@@ -102,6 +103,10 @@ void main() async {
create: (_) => LedgerAccountsProvider(LedgerService()), create: (_) => LedgerAccountsProvider(LedgerService()),
update: (context, organizations, provider) => provider!..update(organizations), update: (context, organizations, provider) => provider!..update(organizations),
), ),
ChangeNotifierProxyProvider<LedgerAccountsProvider, LedgerBalanceMaskController>(
create: (_) => LedgerBalanceMaskController(),
update: (context, ledger, controller) => controller!..update(ledger),
),
ChangeNotifierProxyProvider<WalletsProvider, WalletsController>( ChangeNotifierProxyProvider<WalletsProvider, WalletsController>(
create: (_) => WalletsController(), create: (_) => WalletsController(),
update: (_, wallets, controller) => controller!..update(wallets), update: (_, wallets, controller) => controller!..update(wallets),

View File

@@ -2,7 +2,6 @@ import 'package:pshared/models/currency.dart';
import 'package:pshared/models/payment/chain_network.dart'; import 'package:pshared/models/payment/chain_network.dart';
const String orgOwnerRef = '';
const Currency managedCurrencyDefault = Currency.usdt; const Currency managedCurrencyDefault = Currency.usdt;
const Currency ledgerCurrencyDefault = Currency.rub; const Currency ledgerCurrencyDefault = Currency.rub;
const ChainNetwork managedNetworkDefault = ChainNetwork.tronMainnet; const ChainNetwork managedNetworkDefault = ChainNetwork.tronMainnet;

View File

@@ -11,7 +11,6 @@ import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/wallet/chain_asset.dart'; import 'package:pshared/models/wallet/chain_asset.dart';
import 'package:pshared/provider/accounts/employees.dart'; import 'package:pshared/provider/accounts/employees.dart';
import 'package:pshared/provider/ledger.dart'; import 'package:pshared/provider/ledger.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/payment/wallets.dart'; import 'package:pshared/provider/payment/wallets.dart';
import 'package:pshared/utils/currency.dart'; import 'package:pshared/utils/currency.dart';
@@ -42,7 +41,7 @@ class _AddBalanceDialogState extends State<AddBalanceDialog> {
final _descriptionController = TextEditingController(); final _descriptionController = TextEditingController();
PaymentType _assetType = PaymentType.managedWallet; PaymentType _assetType = PaymentType.managedWallet;
String _ownerRef = orgOwnerRef; String? _ownerRef;
Currency _managedCurrency = managedCurrencyDefault; Currency _managedCurrency = managedCurrencyDefault;
ChainNetwork _network = managedNetworkDefault; ChainNetwork _network = managedNetworkDefault;
Currency _ledgerCurrency = ledgerCurrencyDefault; Currency _ledgerCurrency = ledgerCurrencyDefault;
@@ -59,10 +58,7 @@ class _AddBalanceDialogState extends State<AddBalanceDialog> {
setState(() => _assetType = value); setState(() => _assetType = value);
} }
void _setOwnerRef(String? value) { void _setOwnerRef(String? value) => setState(() => _ownerRef = value);
if (value == null) return;
setState(() => _ownerRef = value);
}
void _setManagedCurrency(Currency? value) { void _setManagedCurrency(Currency? value) {
if (value == null) return; if (value == null) return;
@@ -87,13 +83,17 @@ class _AddBalanceDialogState extends State<AddBalanceDialog> {
final name = _nameController.text.trim(); final name = _nameController.text.trim();
final description = _descriptionController.text.trim(); final description = _descriptionController.text.trim();
final employees = context.read<EmployeesProvider>().employees; final employees = context.read<EmployeesProvider>().employees;
final effectiveOwnerRef = employees.any((employee) => employee.id == _ownerRef) ? _ownerRef : orgOwnerRef; final effectiveOwnerRef = employees.any((employee) => employee.id == _ownerRef)
final isOrgWallet = effectiveOwnerRef == orgOwnerRef; ? _ownerRef
final owner = isOrgWallet ? null : employees.firstWhereOrNull((employee) => employee.id == effectiveOwnerRef); : null;
final isOrgWallet = effectiveOwnerRef == null;
final owner = isOrgWallet
? null
: employees.firstWhereOrNull((employee) => employee.id == effectiveOwnerRef);
final errorMessage = _assetType == PaymentType.managedWallet final errorMessage = _assetType == PaymentType.managedWallet
? l10n.errorCreateManagedWallet ? l10n.errorCreateManagedWallet
: l10n.errorCreateLedgerAccount; : l10n.errorCreateLedgerAccount;
final result = await executeActionWithNotification<bool>( final result = await executeActionWithNotification<bool>(
context: context, context: context,
@@ -124,18 +124,18 @@ class _AddBalanceDialogState extends State<AddBalanceDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
final orgName = context.select<OrganizationsProvider, String?>( final screenWidth = MediaQuery.of(context).size.width;
(provider) => provider.isOrganizationSet ? provider.current.name : null, final maxWidth = (screenWidth - 48).clamp(0.0, double.infinity);
); final dialogWidth = maxWidth >= 360.0 ? (maxWidth > 520.0 ? 520.0 : maxWidth) : maxWidth;
final employeesProvider = context.watch<EmployeesProvider>(); final employeesProvider = context.watch<EmployeesProvider>();
final employees = employeesProvider.employees; final employees = employeesProvider.employees;
final isSaving = context.watch<WalletsProvider>().isLoading || final isSaving = context.watch<WalletsProvider>().isLoading ||
context.watch<LedgerAccountsProvider>().isLoading; context.watch<LedgerAccountsProvider>().isLoading;
final ownerItems = <DropdownMenuItem<String>>[ final ownerItems = <DropdownMenuItem<String?>>[
DropdownMenuItem( DropdownMenuItem(
value: orgOwnerRef, value: null,
child: Text(orgName ?? l10n.companyName), child: Text(l10n.organizationOwned),
), ),
...employees.map((employee) => DropdownMenuItem( ...employees.map((employee) => DropdownMenuItem(
value: employee.id, value: employee.id,
@@ -143,29 +143,30 @@ class _AddBalanceDialogState extends State<AddBalanceDialog> {
)), )),
]; ];
final resolvedOwnerRef = ownerItems.any((item) => item.value == _ownerRef) final resolvedOwnerRef = ownerItems.any((item) => item.value == _ownerRef) ? _ownerRef : null;
? _ownerRef
: orgOwnerRef;
return AlertDialog( return AlertDialog(
title: Text(l10n.actionAddNew), title: Text(l10n.actionAddNew),
content: AddBalanceForm( content: SizedBox(
formKey: _formKey, width: dialogWidth,
assetType: _assetType, child: AddBalanceForm(
isSaving: isSaving, formKey: _formKey,
ownerItems: ownerItems, assetType: _assetType,
ownerValue: resolvedOwnerRef, isSaving: isSaving,
onAssetTypeChanged: _setAssetType, ownerItems: ownerItems,
onOwnerChanged: _setOwnerRef, ownerValue: resolvedOwnerRef,
nameController: _nameController, onAssetTypeChanged: _setAssetType,
descriptionController: _descriptionController, onOwnerChanged: _setOwnerRef,
managedCurrency: _managedCurrency, nameController: _nameController,
network: _network, descriptionController: _descriptionController,
ledgerCurrency: _ledgerCurrency, managedCurrency: _managedCurrency,
onManagedCurrencyChanged: _setManagedCurrency, network: _network,
onNetworkChanged: _setNetwork, ledgerCurrency: _ledgerCurrency,
onLedgerCurrencyChanged: _setLedgerCurrency, onManagedCurrencyChanged: _setManagedCurrency,
showEmployeesLoading: employeesProvider.isLoading, onNetworkChanged: _setNetwork,
onLedgerCurrencyChanged: _setLedgerCurrency,
showEmployeesLoading: employeesProvider.isLoading,
),
), ),
actions: [ actions: [
DialogCancelButton( DialogCancelButton(

View File

@@ -17,8 +17,8 @@ class AddBalanceForm extends StatelessWidget {
final GlobalKey<FormState> formKey; final GlobalKey<FormState> formKey;
final PaymentType assetType; final PaymentType assetType;
final bool isSaving; final bool isSaving;
final List<DropdownMenuItem<String>> ownerItems; final List<DropdownMenuItem<String?>> ownerItems;
final String ownerValue; final String? ownerValue;
final ValueChanged<PaymentType?> onAssetTypeChanged; final ValueChanged<PaymentType?> onAssetTypeChanged;
final ValueChanged<String?> onOwnerChanged; final ValueChanged<String?> onOwnerChanged;
final TextEditingController nameController; final TextEditingController nameController;

View File

@@ -6,8 +6,8 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
class OwnerField extends StatelessWidget { class OwnerField extends StatelessWidget {
final String value; final String? value;
final List<DropdownMenuItem<String>> items; final List<DropdownMenuItem<String?>> items;
final ValueChanged<String?>? onChanged; final ValueChanged<String?>? onChanged;
const OwnerField({ const OwnerField({
@@ -18,9 +18,9 @@ class OwnerField extends StatelessWidget {
}); });
@override @override
Widget build(BuildContext context) => DropdownButtonFormField<String>( Widget build(BuildContext context) => DropdownButtonFormField<String?>(
initialValue: value, value: value,
decoration: getInputDecoration(context, AppLocalizations.of(context)!.colDataOwner, true), decoration: getInputDecoration(context, AppLocalizations.of(context)!.assetOwner, true),
items: items, items: items,
onChanged: onChanged, onChanged: onChanged,
); );

View File

@@ -2,19 +2,19 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/models/payment/wallet.dart'; import 'package:pshared/models/payment/wallet.dart';
import 'package:pshared/utils/currency.dart'; import 'package:pshared/utils/currency.dart';
class BalanceAmount extends StatelessWidget { class BalanceAmount extends StatelessWidget {
final Wallet wallet; final Wallet wallet;
final VoidCallback onToggleVisibility; final VoidCallback onToggleMask;
const BalanceAmount({ const BalanceAmount({
super.key, super.key,
required this.wallet, required this.wallet,
required this.onToggleVisibility, required this.onToggleMask,
}); });
static const double _iconSpacing = 12.0; static const double _iconSpacing = 12.0;
@@ -25,13 +25,13 @@ class BalanceAmount extends StatelessWidget {
final textTheme = Theme.of(context).textTheme; final textTheme = Theme.of(context).textTheme;
final colorScheme = Theme.of(context).colorScheme; final colorScheme = Theme.of(context).colorScheme;
final currencyBalance = currencyCodeToSymbol(wallet.currency); final currencyBalance = currencyCodeToSymbol(wallet.currency);
final wallets = context.read<WalletsController>(); final wallets = context.watch<WalletsController>();
final isMasked = wallets.isBalanceMasked(wallet.id);
return Row( return Row(
children: [ children: [
Text( Text(
isMasked ? '•••• $currencyBalance' : '${amountToString(wallet.balance)} $currencyBalance',
wallets.isHidden(wallet.id) ? '•••• $currencyBalance' : '${amountToString(wallet.balance)} $currencyBalance',
style: textTheme.headlineSmall?.copyWith( style: textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: colorScheme.onSurface, color: colorScheme.onSurface,
@@ -39,9 +39,9 @@ class BalanceAmount extends StatelessWidget {
), ),
const SizedBox(width: _iconSpacing), const SizedBox(width: _iconSpacing),
GestureDetector( GestureDetector(
onTap: onToggleVisibility, onTap: onToggleMask,
child: Icon( child: Icon(
wallets.isHidden(wallet.id) ? Icons.visibility_off : Icons.visibility, isMasked ? Icons.visibility_off : Icons.visibility,
size: _iconSize, size: _iconSize,
color: colorScheme.onSurface, color: colorScheme.onSurface,
), ),
@@ -49,4 +49,4 @@ class BalanceAmount extends StatelessWidget {
], ],
); );
} }
} }

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/provider/ledger.dart'; import 'package:pshared/provider/ledger.dart';
import 'package:pshared/models/payment/wallet.dart'; import 'package:pshared/models/payment/wallet.dart';

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/models/payment/wallet.dart'; import 'package:pshared/models/payment/wallet.dart';
import 'package:pshared/models/payment/chain_network.dart'; import 'package:pshared/models/payment/chain_network.dart';
import 'package:pshared/utils/l10n/chain.dart'; import 'package:pshared/utils/l10n/chain.dart';
@@ -56,8 +56,8 @@ class WalletCard extends StatelessWidget {
children: [ children: [
BalanceAmount( BalanceAmount(
wallet: wallet, wallet: wallet,
onToggleVisibility: () { onToggleMask: () {
context.read<WalletsController>().toggleVisibility(wallet.id); context.read<WalletsController>().toggleBalanceMask(wallet.id);
}, },
), ),
WalletBalanceRefreshButton( WalletBalanceRefreshButton(

View File

@@ -1,13 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/controllers/balance_mask/ledger_accounts.dart';
import 'package:pshared/models/ledger/account.dart'; import 'package:pshared/models/ledger/account.dart';
import 'package:pshared/utils/currency.dart'; import 'package:pshared/utils/currency.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
import 'package:pweb/pages/dashboard/buttons/balance/config.dart'; import 'package:pweb/pages/dashboard/buttons/balance/config.dart';
import 'package:pweb/pages/dashboard/buttons/balance/header.dart'; import 'package:pweb/pages/dashboard/buttons/balance/header.dart';
import 'package:pweb/widgets/refresh_balance/ledger.dart'; import 'package:pweb/widgets/refresh_balance/ledger.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class LedgerAccountCard extends StatelessWidget { class LedgerAccountCard extends StatelessWidget {
final LedgerAccount account; final LedgerAccount account;
@@ -38,6 +42,20 @@ class LedgerAccountCard extends StatelessWidget {
} }
} }
String _formatMaskedBalance() {
final currency = account.currency.trim();
if (currency.isEmpty) return '••••';
try {
final symbol = currencyCodeToSymbol(currencyStringToCode(currency));
if (symbol.trim().isEmpty) {
return '•••• $currency';
}
return '•••• $symbol';
} catch (_) {
return '•••• $currency';
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme; final textTheme = Theme.of(context).textTheme;
@@ -64,12 +82,30 @@ class LedgerAccountCard extends StatelessWidget {
), ),
Row( Row(
children: [ children: [
Text( Consumer<LedgerBalanceMaskController>(
_formatBalance(), builder: (context, controller, _) {
style: textTheme.headlineSmall?.copyWith( final isMasked = controller.isBalanceMasked(account.ledgerAccountRef);
fontWeight: FontWeight.bold, return Row(
color: colorScheme.onSurface, children: [
), Text(
isMasked ? _formatMaskedBalance() : _formatBalance(),
style: textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
const SizedBox(width: 12),
GestureDetector(
onTap: () => controller.toggleBalanceMask(account.ledgerAccountRef),
child: Icon(
isMasked ? Icons.visibility_off : Icons.visibility,
size: 24,
color: colorScheme.onSurface,
),
),
],
);
},
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
LedgerBalanceRefreshButton( LedgerBalanceRefreshButton(

View File

@@ -3,9 +3,9 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/models/payment/quote/status_type.dart'; import 'package:pshared/models/payment/quote/status_type.dart';
import 'package:pweb/providers/quotation/quotation.dart';
import 'package:pweb/pages/dashboard/payouts/quote_status/widgets/body.dart'; import 'package:pweb/pages/dashboard/payouts/quote_status/widgets/body.dart';
import 'package:pweb/providers/quotation/quotation.dart';
import 'package:pweb/utils/quote_duration_format.dart'; import 'package:pweb/utils/quote_duration_format.dart';
import 'package:pweb/generated/i18n/app_localizations.dart'; import 'package:pweb/generated/i18n/app_localizations.dart';

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/utils/currency.dart'; import 'package:pshared/utils/currency.dart';
import 'package:pweb/pages/dashboard/payouts/summary/fee.dart'; import 'package:pweb/pages/dashboard/payouts/summary/fee.dart';

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/models/payment/type.dart'; import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/recipient/recipient.dart'; import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/payment/flow.dart'; import 'package:pshared/provider/payment/flow.dart';

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/models/payment/wallet.dart'; import 'package:pshared/models/payment/wallet.dart';
import 'package:pshared/models/recipient/recipient.dart'; import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/recipient/provider.dart'; import 'package:pshared/provider/recipient/provider.dart';

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/models/payment/wallet.dart'; import 'package:pshared/models/payment/wallet.dart';
import 'package:pweb/utils/payment/dropdown.dart'; import 'package:pweb/utils/payment/dropdown.dart';

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/utils/currency.dart'; import 'package:pshared/utils/currency.dart';
import 'package:pshared/models/payment/wallet.dart'; import 'package:pshared/models/payment/wallet.dart';
@@ -50,8 +50,8 @@ class WalletCard extends StatelessWidget {
children: [ children: [
BalanceAmount( BalanceAmount(
wallet: wallet, wallet: wallet,
onToggleVisibility: () { onToggleMask: () {
context.read<WalletsController>().toggleVisibility(wallet.id); context.read<WalletsController>().toggleBalanceMask(wallet.id);
}, },
), ),
WalletBalanceRefreshButton( WalletBalanceRefreshButton(

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/models/payment/type.dart'; import 'package:pshared/models/payment/type.dart';
import 'package:pweb/app/router/payout_routes.dart'; import 'package:pweb/app/router/payout_routes.dart';

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pweb/app/router/payout_routes.dart'; import 'package:pweb/app/router/payout_routes.dart';
import 'package:pweb/widgets/sidebar/destinations.dart'; import 'package:pweb/widgets/sidebar/destinations.dart';

View File

@@ -3,7 +3,7 @@ import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pweb/pages/dashboard/buttons/balance/amount.dart'; import 'package:pweb/pages/dashboard/buttons/balance/amount.dart';
import 'package:pweb/widgets/refresh_balance/wallet.dart'; import 'package:pweb/widgets/refresh_balance/wallet.dart';
@@ -30,7 +30,7 @@ class WalletEditFields extends StatelessWidget {
Expanded( Expanded(
child: BalanceAmount( child: BalanceAmount(
wallet: wallet, wallet: wallet,
onToggleVisibility: () => controller.toggleVisibility(wallet.id), onToggleMask: () => controller.toggleBalanceMask(wallet.id),
), ),
), ),
WalletBalanceRefreshButton(walletRef: wallet.id), WalletBalanceRefreshButton(walletRef: wallet.id),

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pweb/generated/i18n/app_localizations.dart'; import 'package:pweb/generated/i18n/app_localizations.dart';

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pweb/pages/payout_page/wallet/edit/buttons/buttons.dart'; import 'package:pweb/pages/payout_page/wallet/edit/buttons/buttons.dart';
import 'package:pweb/pages/payout_page/wallet/edit/fields.dart'; import 'package:pweb/pages/payout_page/wallet/edit/fields.dart';

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/controllers/wallets.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pweb/pages/wallet_top_up/content.dart'; import 'package:pweb/pages/wallet_top_up/content.dart';