summaryrefslogtreecommitdiffstats
path: root/lib/ledger.ml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ledger.ml')
-rw-r--r--lib/ledger.ml112
1 files changed, 69 insertions, 43 deletions
diff --git a/lib/ledger.ml b/lib/ledger.ml
index 9d315ae..d63dbed 100644
--- a/lib/ledger.ml
+++ b/lib/ledger.ml
@@ -395,21 +395,16 @@ module Account_hierarchy = struct
395 395
396 let rec unsafe_alter_aux (subaid : Account_path.t) 396 let rec unsafe_alter_aux (subaid : Account_path.t)
397 (f : 397 (f :
398 Account_type.t0 -> 398 Account_type.t0 -> Commodity_id.t -> Money.Diff.t -> 'a * Money.Diff.t)
399 extra -> 399 : 'a Mapper.t =
400 Commodity_id.t ->
401 Money.Diff.t ->
402 'a * extra * Money.Diff.t) : 'a Mapper.t =
403 { 400 {
404 car = 401 car =
405 (fun in_acc mkt -> 402 (fun in_acc mkt ->
406 let open Option.Let_syntax in 403 let open Option.Let_syntax in
407 match (subaid, in_acc) with 404 match (subaid, in_acc) with
408 | [], (extra, Leaf (acc_comm, acc_bal)) -> 405 | [], (extra, Leaf (acc_comm, acc_bal)) ->
409 let x, extra', acc_bal' = 406 let x, acc_bal' = f (mkt Account_type.Leaf) acc_comm acc_bal in
410 f (mkt Account_type.Leaf) extra acc_comm acc_bal 407 Some (x, (extra, Leaf (acc_comm, acc_bal')))
411 in
412 Some (x, (extra', Leaf (acc_comm, acc_bal')))
413 | [], _ -> None 408 | [], _ -> None
414 | subaid0 :: subaid, (extra, Node subaccs) -> 409 | subaid0 :: subaid, (extra, Node subaccs) ->
415 let%bind subacc = Map.find subaccs subaid0 in 410 let%bind subacc = Map.find subaccs subaid0 in
@@ -429,11 +424,8 @@ module Account_hierarchy = struct
429 424
430 let unsafe_alter (aid : Account_path.t) 425 let unsafe_alter (aid : Account_path.t)
431 (f : 426 (f :
432 Account_type.t0 -> 427 Account_type.t0 -> Commodity_id.t -> Money.Diff.t -> 'a * Money.Diff.t)
433 extra -> 428 (w : world) : ('a * world) option =
434 Commodity_id.t ->
435 Money.Diff.t ->
436 'a * extra * Money.Diff.t) (w : world) : ('a * world) option =
437 match aid with 429 match aid with
438 | [] -> None 430 | [] -> None
439 | aid0 :: subaid -> ( 431 | aid0 :: subaid -> (
@@ -466,16 +458,18 @@ module Account_hierarchy = struct
466 (x, Map.set w ~key:aid0 ~data:(Equity acc'))) 458 (x, Map.set w ~key:aid0 ~data:(Equity acc')))
467 459
468 (** Update the balance (debit/credit ([dc])) of account [aid] [by_amount] 460 (** Update the balance (debit/credit ([dc])) of account [aid] [by_amount]
469 (commodity: [in_comm]) in [world], giving the updated world and the pre 461 (commodity: [in_comm]) in [world], giving the updated world and the post
470 and post balances for [aid] iff the account exists in [world]. *) 462 balance for [aid] iff the account exists in [world].
471 let update_bal aid dc by_amount in_comm (w : world) : 463
472 (Money.Diff.t * Money.Diff.t * world) option = 464 Unsafe because [by_amount] must be balanced with updates to other
465 accounts. *)
466 let unsafe_update_bal aid dc by_amount in_comm (w : world) :
467 (Money.Diff.t * world) option =
473 let open Option.Let_syntax in 468 let open Option.Let_syntax in
474 let%bind mres, w' = 469 let%bind mres, w' =
475 unsafe_alter aid 470 unsafe_alter aid
476 (fun acc_type acc_extra acc_comm acc_bal -> 471 (fun acc_type acc_comm acc_bal ->
477 if not ([%equal: Commodity_id.t] acc_comm in_comm) then 472 if not ([%equal: Commodity_id.t] acc_comm in_comm) then (None, acc_bal)
478 (None, acc_extra, acc_bal)
479 else 473 else
480 let on_debit = 474 let on_debit =
481 match acc_type with 475 match acc_type with
@@ -488,41 +482,68 @@ module Account_hierarchy = struct
488 let acc_bal' = 482 let acc_bal' =
489 Money.Diff.(acc_bal + of_amount by_amount dc ~on_debit) 483 Money.Diff.(acc_bal + of_amount by_amount dc ~on_debit)
490 in 484 in
491 (Some (acc_bal, acc_bal'), acc_extra, acc_bal')) 485 (Some acc_bal', acc_bal'))
492 w 486 w
493 in 487 in
494 let%map pre_bal, post_bal = mres in 488 let%map post_bal = mres in
495 (pre_bal, post_bal, w') 489 (post_bal, w')
496
497 let get_bal aid (w : world) : (Commodity_id.t * Money.Diff.t) option =
498 let open Option.Let_syntax in
499 let%map cb, _world' =
500 unsafe_alter aid
501 (fun _acc_type acc_extra acc_comm acc_bal ->
502 ((acc_comm, acc_bal), acc_extra, acc_bal))
503 w
504 in
505 cb
506 490
507 let add_balance_maps m1 m2 : Money.Diff.t Commodity_id.Map.t = 491 let add_balance_maps m1 m2 : Money.Diff.t Commodity_id.Map.t =
508 Map.merge m1 m2 ~f:(fun ~key:_comm -> function 492 Map.merge m1 m2 ~f:(fun ~key:_comm -> function
509 | `Both (b1, b2) -> Some Money.Diff.(b1 + b2) 493 | `Both (b1, b2) -> Some Money.Diff.(b1 + b2)
510 | `Left b | `Right b -> Some b) 494 | `Left b | `Right b -> Some b)
511 495
512 let rec collect_balances : type a. 496 module Account_visitor = struct
497 type 'res t = { car : 'a. 'a Structure.f account -> 'res }
498
499 let rec visit_aux : type a.
500 'res t -> Account_path.t * a Structure.f account -> 'res option =
501 fun visitor -> function
502 | [], in_acc -> Some (visitor.car in_acc)
503 | _ :: _, (_, Leaf _) -> None
504 | subaid0 :: subaid, (_, Ind subaccs) ->
505 let open Option.Let_syntax in
506 let%bind subacc = Map.find subaccs subaid0 in
507 visit_aux visitor (subaid, subacc)
508 | subaid0 :: subaid, (_, Node subaccs) ->
509 let open Option.Let_syntax in
510 let module Visitor = Account_structure.Basic_visitor (Structure) in
511 let%bind subacc = Map.find subaccs subaid0 in
512 Visitor.visit
513 { car = (fun subacc -> (visit_aux visitor) (subaid, subacc)) }
514 subacc
515
516 let visit (visitor : 'res t) (w : world) : Account_path.t -> 'res option =
517 function
518 | [] -> None
519 | aid0 :: subaid -> (
520 let open Option.Let_syntax in
521 match%bind Map.find w aid0 with
522 | Asset acc -> visit_aux visitor (subaid, acc)
523 | Expense acc -> visit_aux visitor (subaid, acc)
524 | Equity acc -> visit_aux visitor (subaid, acc)
525 | Liability acc -> visit_aux visitor (subaid, acc)
526 | Income acc -> visit_aux visitor (subaid, acc))
527 end
528
529 let rec collect_balances_aux : type a.
513 a Structure.f account -> Money.Diff.t Commodity_id.Map.t = function 530 a Structure.f account -> Money.Diff.t Commodity_id.Map.t = function
514 | _extra, Leaf (acc_comm, acc_bal) -> 531 | _extra, Leaf (acc_comm, acc_bal) ->
515 Commodity_id.Map.singleton acc_comm acc_bal 532 Commodity_id.Map.singleton acc_comm acc_bal
516 | _extra, Ind subaccs -> 533 | _extra, Ind subaccs ->
517 Map.fold subaccs ~init:Commodity_id.Map.empty 534 Map.fold subaccs ~init:Commodity_id.Map.empty
518 ~f:(fun ~key:_ ~data:subacc comm_bal_sums -> 535 ~f:(fun ~key:_ ~data:subacc comm_bal_sums ->
519 add_balance_maps comm_bal_sums (collect_balances subacc)) 536 add_balance_maps comm_bal_sums (collect_balances_aux subacc))
520 | _extra, Node subaccs -> 537 | _extra, Node subaccs ->
521 Map.fold subaccs ~init:Commodity_id.Map.empty 538 Map.fold subaccs ~init:Commodity_id.Map.empty
522 ~f:(fun ~key:_ ~data:subacc comm_bal_sums -> 539 ~f:(fun ~key:_ ~data:subacc comm_bal_sums ->
523 let module Visitor = Account_structure.Basic_visitor (Structure) in 540 let module Visitor = Account_structure.Basic_visitor (Structure) in
524 add_balance_maps comm_bal_sums 541 add_balance_maps comm_bal_sums
525 (Visitor.visit { car = collect_balances } subacc)) 542 (Visitor.visit { car = collect_balances_aux } subacc))
543
544 let collect_balances :
545 world -> Account_path.t -> Money.Diff.t Commodity_id.Map.t option =
546 Account_visitor.visit { car = collect_balances_aux }
526 547
527 type delete_error = Not_found | Nonzero_balance 548 type delete_error = Not_found | Nonzero_balance
528 549
@@ -569,7 +590,11 @@ module Account_hierarchy = struct
569end 590end
570 591
571module Bal_assert = struct 592module Bal_assert = struct
572 type t = { account : Account_path.t; labels : Labels.t; bal : Money.Diff.t } 593 type t = {
594 account : Account_path.t;
595 labels : Labels.t;
596 bals : Money.Diff.t Commodity_id.Map.t;
597 }
573 [@@deriving sexp_of] 598 [@@deriving sexp_of]
574end 599end
575 600
@@ -658,9 +683,9 @@ module World = struct
658 Map.fold_option tx.entries ~init:world 683 Map.fold_option tx.entries ~init:world
659 ~f:(fun ~key:aid ~(data : Tx.entry) world -> 684 ~f:(fun ~key:aid ~(data : Tx.entry) world ->
660 let open Option.Let_syntax in 685 let open Option.Let_syntax in
661 let%bind _old_bal, new_bal, world = 686 let%bind new_bal, world =
662 Account_hierarchy.update_bal aid data.dc data.amount data.commodity 687 Account_hierarchy.unsafe_update_bal aid data.dc data.amount
663 world 688 data.commodity world
664 in 689 in
665 match data.assertion with 690 match data.assertion with
666 | None -> Some world 691 | None -> Some world
@@ -669,8 +694,9 @@ module World = struct
669 694
670 let apply_ba (ba : Bal_assert.t) world : t option = 695 let apply_ba (ba : Bal_assert.t) world : t option =
671 let open Option.Let_syntax in 696 let open Option.Let_syntax in
672 let%bind _comm, bal = Account_hierarchy.get_bal ba.account world in 697 let%bind bals = Account_hierarchy.collect_balances world ba.account in
673 if not Money.Diff.(bal = ba.bal) then None else Some world 698 if not ([%equal: Money.Diff.t Commodity_id.Map.t] bals ba.bals) then None
699 else Some world
674 700
675 let apply_ad (_ad : Account_decl.t) _world : t option = None 701 let apply_ad (_ad : Account_decl.t) _world : t option = None
676 702