Functional Weave
Code in Rust

legal.time-billing@1.0.0

vectors.json

15,636 bytes · view raw

[
 {
  "name": "a matter's time in 6-minute units, part units charged in full",
  "args": [
   [
    {
     "feeEarner": "PTN",
     "minutes": 20
    },
    {
     "feeEarner": "AST",
     "minutes": 61
    },
    {
     "feeEarner": "TRN",
     "minutes": 6
    },
    {
     "feeEarner": "PTN",
     "minutes": 7
    }
   ],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     }
    }
   ],
   6,
   "up"
  ],
  "expect": {
   "lines": [
    {
     "feeEarner": "PTN",
     "minutes": 20,
     "billedMinutes": 24,
     "units": 4,
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 14000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "minutes": 61,
     "billedMinutes": 66,
     "units": 11,
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 24200,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "minutes": 6,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 1500,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "PTN",
     "minutes": 7,
     "billedMinutes": 12,
     "units": 2,
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 7000,
      "currency": "GBP"
     }
    }
   ],
   "byFeeEarner": [
    {
     "feeEarner": "PTN",
     "units": 6,
     "fee": {
      "minor": 21000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "units": 11,
     "fee": {
      "minor": 24200,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "units": 1,
     "fee": {
      "minor": 1500,
      "currency": "GBP"
     }
    }
   ],
   "units": 18,
   "billedMinutes": 108,
   "total": {
    "minor": 46700,
    "currency": "GBP"
   }
  }
 },
 {
  "name": "the same time rounded to the nearest unit",
  "args": [
   [
    {
     "feeEarner": "PTN",
     "minutes": 20
    },
    {
     "feeEarner": "AST",
     "minutes": 61
    },
    {
     "feeEarner": "TRN",
     "minutes": 6
    },
    {
     "feeEarner": "PTN",
     "minutes": 7
    }
   ],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     }
    }
   ],
   6,
   "half-up"
  ],
  "expect": {
   "lines": [
    {
     "feeEarner": "PTN",
     "minutes": 20,
     "billedMinutes": 18,
     "units": 3,
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 10500,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "minutes": 61,
     "billedMinutes": 60,
     "units": 10,
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "minutes": 6,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 1500,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "PTN",
     "minutes": 7,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 3500,
      "currency": "GBP"
     }
    }
   ],
   "byFeeEarner": [
    {
     "feeEarner": "PTN",
     "units": 4,
     "fee": {
      "minor": 14000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "units": 10,
     "fee": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "units": 1,
     "fee": {
      "minor": 1500,
      "currency": "GBP"
     }
    }
   ],
   "units": 15,
   "billedMinutes": 90,
   "total": {
    "minor": 37500,
    "currency": "GBP"
   }
  }
 },
 {
  "name": "seven 1-minute emails are seven units, not the two a rounded total gives",
  "args": [
   [
    {
     "feeEarner": "AST",
     "minutes": 1
    },
    {
     "feeEarner": "AST",
     "minutes": 1
    },
    {
     "feeEarner": "AST",
     "minutes": 1
    },
    {
     "feeEarner": "AST",
     "minutes": 1
    },
    {
     "feeEarner": "AST",
     "minutes": 1
    },
    {
     "feeEarner": "AST",
     "minutes": 1
    },
    {
     "feeEarner": "AST",
     "minutes": 1
    }
   ],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     }
    }
   ],
   6,
   "up"
  ],
  "expect": {
   "lines": [
    {
     "feeEarner": "AST",
     "minutes": 1,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 2200,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "minutes": 1,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 2200,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "minutes": 1,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 2200,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "minutes": 1,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 2200,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "minutes": 1,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 2200,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "minutes": 1,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 2200,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "minutes": 1,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 2200,
      "currency": "GBP"
     }
    }
   ],
   "byFeeEarner": [
    {
     "feeEarner": "AST",
     "units": 7,
     "fee": {
      "minor": 15400,
      "currency": "GBP"
     }
    }
   ],
   "units": 7,
   "billedMinutes": 42,
   "total": {
    "minor": 15400,
    "currency": "GBP"
   }
  }
 },
 {
  "name": "quarter-hour units",
  "args": [
   [
    {
     "feeEarner": "PTN",
     "minutes": 16
    },
    {
     "feeEarner": "PTN",
     "minutes": 45
    }
   ],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     }
    }
   ],
   15,
   "up"
  ],
  "expect": {
   "lines": [
    {
     "feeEarner": "PTN",
     "minutes": 16,
     "billedMinutes": 30,
     "units": 2,
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 17500,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "PTN",
     "minutes": 45,
     "billedMinutes": 45,
     "units": 3,
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 26250,
      "currency": "GBP"
     }
    }
   ],
   "byFeeEarner": [
    {
     "feeEarner": "PTN",
     "units": 5,
     "fee": {
      "minor": 43750,
      "currency": "GBP"
     }
    }
   ],
   "units": 5,
   "billedMinutes": 75,
   "total": {
    "minor": 43750,
    "currency": "GBP"
   }
  }
 },
 {
  "name": "a unit at an odd hourly rate rounds half-up to the penny: 177.77 an hour is 17.78 a unit",
  "args": [
   [
    {
     "feeEarner": "CON",
     "minutes": 6
    },
    {
     "feeEarner": "CON",
     "minutes": 5
    }
   ],
   [
    {
     "feeEarner": "CON",
     "hourlyRate": {
      "minor": 17777,
      "currency": "GBP"
     }
    }
   ],
   6,
   "up"
  ],
  "expect": {
   "lines": [
    {
     "feeEarner": "CON",
     "minutes": 6,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 17777,
      "currency": "GBP"
     },
     "fee": {
      "minor": 1778,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "CON",
     "minutes": 5,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 17777,
      "currency": "GBP"
     },
     "fee": {
      "minor": 1778,
      "currency": "GBP"
     }
    }
   ],
   "byFeeEarner": [
    {
     "feeEarner": "CON",
     "units": 2,
     "fee": {
      "minor": 3556,
      "currency": "GBP"
     }
    }
   ],
   "units": 2,
   "billedMinutes": 12,
   "total": {
    "minor": 3556,
    "currency": "GBP"
   }
  }
 },
 {
  "name": "a zero-minute entry is kept as a nil line",
  "args": [
   [
    {
     "feeEarner": "TRN",
     "minutes": 0
    }
   ],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     }
    }
   ],
   6,
   "up"
  ],
  "expect": {
   "lines": [
    {
     "feeEarner": "TRN",
     "minutes": 0,
     "billedMinutes": 0,
     "units": 0,
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 0,
      "currency": "GBP"
     }
    }
   ],
   "byFeeEarner": [
    {
     "feeEarner": "TRN",
     "units": 0,
     "fee": {
      "minor": 0,
      "currency": "GBP"
     }
    }
   ],
   "units": 0,
   "billedMinutes": 0,
   "total": {
    "minor": 0,
    "currency": "GBP"
   }
  }
 },
 {
  "name": "no entries bill nothing, in the rate card's currency",
  "args": [
   [],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 40000,
      "currency": "EUR"
     }
    }
   ],
   6,
   "up"
  ],
  "expect": {
   "lines": [],
   "byFeeEarner": [],
   "units": 0,
   "billedMinutes": 0,
   "total": {
    "minor": 0,
    "currency": "EUR"
   }
  }
 },
 {
  "name": "a fee earner's summary follows their first entry",
  "args": [
   [
    {
     "feeEarner": "TRN",
     "minutes": 12
    },
    {
     "feeEarner": "PTN",
     "minutes": 6
    },
    {
     "feeEarner": "TRN",
     "minutes": 6
    }
   ],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     }
    }
   ],
   6,
   "up"
  ],
  "expect": {
   "lines": [
    {
     "feeEarner": "TRN",
     "minutes": 12,
     "billedMinutes": 12,
     "units": 2,
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 3000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "PTN",
     "minutes": 6,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 3500,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "minutes": 6,
     "billedMinutes": 6,
     "units": 1,
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     },
     "fee": {
      "minor": 1500,
      "currency": "GBP"
     }
    }
   ],
   "byFeeEarner": [
    {
     "feeEarner": "TRN",
     "units": 3,
     "fee": {
      "minor": 4500,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "PTN",
     "units": 1,
     "fee": {
      "minor": 3500,
      "currency": "GBP"
     }
    }
   ],
   "units": 4,
   "billedMinutes": 24,
   "total": {
    "minor": 8000,
    "currency": "GBP"
   }
  }
 },
 {
  "name": "an entry whose fee earner has no rate is an error",
  "args": [
   [
    {
     "feeEarner": "XYZ",
     "minutes": 6
    }
   ],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     }
    }
   ],
   6,
   "up"
  ],
  "expectError": "no rate for fee earner \"XYZ\""
 },
 {
  "name": "an empty rate card is an error",
  "args": [
   [],
   [],
   6,
   "up"
  ],
  "expectError": "rates must not be empty"
 },
 {
  "name": "a fee earner twice on the rate card is an error",
  "args": [
   [],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 1,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 2,
      "currency": "GBP"
     }
    }
   ],
   6,
   "up"
  ],
  "expectError": "duplicate rate for fee earner \"PTN\""
 },
 {
  "name": "a rate card in two currencies is an error",
  "args": [
   [],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 1,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 2,
      "currency": "EUR"
     }
    }
   ],
   6,
   "up"
  ],
  "expectError": "currency mismatch"
 },
 {
  "name": "a negative hourly rate is an error",
  "args": [
   [],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": -1,
      "currency": "GBP"
     }
    }
   ],
   6,
   "up"
  ],
  "expectError": "hourly rates must not be negative"
 },
 {
  "name": "negative minutes are an error",
  "args": [
   [
    {
     "feeEarner": "PTN",
     "minutes": -6
    }
   ],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     }
    }
   ],
   6,
   "up"
  ],
  "expectError": "minutes must be a non-negative integer"
 },
 {
  "name": "a zero-minute unit is an error",
  "args": [
   [
    {
     "feeEarner": "PTN",
     "minutes": 6
    }
   ],
   [
    {
     "feeEarner": "PTN",
     "hourlyRate": {
      "minor": 35000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "AST",
     "hourlyRate": {
      "minor": 22000,
      "currency": "GBP"
     }
    },
    {
     "feeEarner": "TRN",
     "hourlyRate": {
      "minor": 15000,
      "currency": "GBP"
     }
    }
   ],
   0,
   "up"
  ],
  "expectError": "increment must be a positive integer"
 }
]