amortizy 3.1.1 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ca0ea325340402016df8375d37a1df326360b5c83757133ab91d7d90586d98b
4
- data.tar.gz: d8323ccba488c8e3fd4ed75095ede9b84615011bcdd881e038b9e539ffc2d694
3
+ metadata.gz: 580f55e8a6cadb029944b8fec559b2f280cbcee6c5452f2c132dd3fa314c0cec
4
+ data.tar.gz: b3c68e7a5f0f9c35536754c1a4e665cef3ae814b358128e59747be1d735ca2de
5
5
  SHA512:
6
- metadata.gz: bfa7d476af355d10e128f26906c102f9fed268c8ac62ade499bb5cefb230d7288579d98e27970271250a7ad6e1d8c6f1fdb9d4230ab06d2ea1e0322af6706e19
7
- data.tar.gz: b2c3bef7394514492942ff87f61f229f74a398337298a3bd9671a01c48ff914357fd50ba2146210b56c7907624b9667c6df491e76699239c2f180b78c0780e2c
6
+ metadata.gz: 05a7f4cb9713cefefa2036f9cc6a0b906601b3bf4d143cb1b76338c040f3b804c313f25a46dd4bf88992bb97883b42842b5729815d77d1c12c24ce64f2372cc4
7
+ data.tar.gz: aa4e32281172160851e5c84d364229ef18e8e4a7e07f1849022cff3734e64db6fc7068f1ea301c1bd0eaae0c2ee95fd0ca40c1a0f3d58be019ae7203de4f3fa0
data/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.1.2] - 2026-10-21
9
+
10
+ ### Fixed
11
+ - `additional_fee_treatment: :separate_payment` no longer gives the borrower an interest-free period (#13). Interest accrues from funding, so the first regular payment includes the fee period's interest, on every frequency. Precomputed interest also counts the fee period
12
+ - Monthly loans with a separate fee now put payment 1 one month after the fee instead of on the fee date, matching the other frequencies. These loans now mature one month later (e.g. `term_months: 12` ends in month 13)
13
+
8
14
  ## [3.1.1] - 2026-10-21
9
15
 
10
16
  ### Fixed
data/README.md CHANGED
@@ -236,7 +236,7 @@ The fee is added to the loan principal upfront, increasing the base amount finan
236
236
  **Best for:** Rolling all costs into the loan amount
237
237
 
238
238
  ### 3. Separate Payment (`:separate_payment`)
239
- The fee is collected as a separate payment (`payment_number: 0`) one period after the start, before regular amortization begins.
239
+ The fee is collected as a separate payment (`payment_number: 0`) one period after the start. Regular payments begin one period after the fee. Interest accrues from funding, so the first regular payment carries the interest for the fee's period as well as its own, and the loan matures one period after `term_months`.
240
240
 
241
241
  **Best for:** Collecting fees upfront separately from the loan repayment
242
242
 
@@ -225,10 +225,10 @@ module Amortizy
225
225
  total_days.to_f / sample_size
226
226
  end
227
227
 
228
+ # A separate fee row takes a period of its own, and interest accrues through it.
228
229
  def estimate_total_loan_days
229
- num = total_payments
230
- avg_days_per_period = calculate_average_days_per_period
231
- num * avg_days_per_period
230
+ periods = total_payments + (separate_fee_payment? ? 1 : 0)
231
+ periods * calculate_average_days_per_period
232
232
  end
233
233
 
234
234
  def calculate_precomputed_total_interest
@@ -313,8 +313,11 @@ module Amortizy
313
313
  ((effective_principal * growth) - @balloon_amount) / payments_value
314
314
  end
315
315
 
316
+ # Interest accrues from funding (or the end of the grace period). A separate
317
+ # fee row collects no interest, so the first regular payment carries the
318
+ # interest for the fee's period too.
316
319
  def amortizing_period_rates
317
- previous_date = first_regular_period_start
320
+ previous_date = first_payment_date
318
321
  rates = regular_payment_dates.map do |date|
319
322
  rate = (@annual_rate / 365.0) * calculate_days_between(previous_date, date)
320
323
  previous_date = date
@@ -339,7 +342,9 @@ module Amortizy
339
342
  # payment date to prevent bank-day adjustments from compounding drift.
340
343
  def regular_payment_dates
341
344
  @regular_payment_dates ||= begin
342
- monthly_target = first_payment_date
345
+ # A separate fee takes the first period, so regular payments start a
346
+ # month after the fee's (unadjusted) target date.
347
+ monthly_target = separate_fee_payment? ? advance_by_month(first_payment_date) : first_payment_date
343
348
  previous_date = first_regular_period_start
344
349
  Array.new(total_payments) do
345
350
  if @frequency == :monthly
@@ -442,7 +447,6 @@ module Amortizy
442
447
  payment_type: 'Additional Fee Payment',
443
448
  days_in_period: 0
444
449
  }
445
- previous_payment_date = payment_date
446
450
  end
447
451
 
448
452
  additional_fee_per_payment = @additional_fee_treatment == :distributed ? (@additional_fee / num) : 0.0
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Amortizy
4
- VERSION = '3.1.1'
4
+ VERSION = '3.1.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amortizy
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rich Zapata