Profiles / Business & IT
AM

Accountant & manager

Invoices with VAT, margins, break-even, depreciation and payment due dates.

10 recipes on this page · open in the interactive view

Recipes
  1. Invoice totals with two VAT rates and the due date
  2. Break-even point and margin of safety
  3. Declining-balance depreciation schedule
  4. Late payment interest and recovery indemnity
  5. Bank reconciliation of account 512
  6. Benford test of expense claims
  7. Gaps and duplicates in the invoice numbering
  8. Aged receivables and doubtful debt allowance
  9. Reciprocal allocation of service department costs
  10. Cash rounding of tickets to 5 cents
executed on MentDB

Invoice totals with two VAT rates and the due date

An invoice dated 15 December 2026 has goods at 20% VAT and books at 5.5%. The script computes the VAT and the gross total, then the due date of a '30 days end of month' term, moved back to the last business day when it falls on a weekend.

Pipeline
Script
admin
#1. Invoice dated 2026-12-15, two VAT rates;
-> "[ht20]" (+ 1250 340);
-> "[ht55]" 180;
-> "[vat]" (math round (+ (math percent [ht20] 20) (math percent [ht55] 5.5)) 2);
-> "[ttc]" (math calc "[ht20]+[ht55]+[vat]");
#2. Term: 30 days end of month;
-> "[due]" (date last_day_of_month (date add "2026-12-15" "DAY" 30));
#3. A weekend due date moves back to the last business day;
-> "[paid]" (date add_business_days (date add_business_days [due] 1) -1);
concat "{\"net\": " (+ [ht20] [ht55]) ", \"vat\": " [vat] ", \"gross\": " [ttc] ", \"term_end\": \"" [due] "\", \"weekend\": " (date is_weekend [due]) ", \"due\": \"" [paid] "\"}";
Result
mentdb
{"net": 1770, "vat": 327.9, "gross": 2097.9, "term_end": "2027-01-31", "weekend": 1, "due": "2027-01-29"}
Go further
executed on MentDB

Break-even point and margin of safety

A product sells 25 with a variable cost of 13.40 and 48000 of fixed costs a year. The script computes the unit contribution margin, the break-even volume and revenue, and the margin of safety against a budget of 5600 units.

Pipeline
Script
admin
#1. Costs and prices of the product;
-> "[fixed]" 48000;
-> "[price]" 25;
-> "[var]" 13.4;
-> "[budget]" 5600;
#2. Contribution margin and break-even point;
-> "[cm]" (math calc "[price]-[var]");
-> "[be]" (math ceil (math calc "[fixed]/[cm]"));
#3. Margin of safety on the budgeted volume;
-> "[mos]" (math round (math calc "([budget]-[be])/[budget]*100") 1);
concat "Contribution margin " [cm] " per unit, " (math round (math calc "[cm]/[price]*100") 1) "% of the price. Break-even at " [be] " units = " (* [be] [price]) " in sales. Margin of safety " [mos] "%."
Result
mentdb
Contribution margin 11.6 per unit, 46.4% of the price. Break-even at 4138 units = 103450 in sales. Margin of safety 26.1%.
Go further
  • Operating leverage: total contribution divided by operating profit at the budget volume
  • Several products: weight the margins by the sales mix with stat weighted_mean
executed on MentDB

Declining-balance depreciation schedule

A machine bought 20000 is depreciated over 5 years with the declining coefficient 1.75 (rate 35%). The script compares the first-year charge with the straight-line method and tables the net book value at the end of each year.

Pipeline
Script
admin
#1. Asset bought 20000, 5 years, declining coefficient 1.75;
-> "[cost]" 20000;
-> "[rate]" (math calc "1.75/5");
#2. Year 1 charge, declining vs straight line;
-> "[y1]" (math calc "[cost]*[rate]");
-> "[sl]" (math calc "[cost]/5");
#3. Net book value at the end of each year;
-> "[nbv]" (num table (concat "round(" [cost] "*(1-" [rate] ")^n*100)/100") "n" 0 5 1);
concat "Year 1 charge: " [y1] " declining vs " [sl] " straight line. Net book value by year: " [nbv];
Result
mentdb
Year 1 charge: 7000 declining vs 4000 straight line. Net book value by year: [
  [0,20000],
  [1,13000],
  [2,8450],
  [3,5492.5],
  [4,3570.13],
  [5,2320.58]
]
Go further
  • Switch to straight line when the remaining value divided by the remaining years gives a higher charge
  • Prorata temporis for the first year: multiply by date nb_day from the purchase date to year end, over 360
executed on MentDB

Late payment interest and recovery indemnity

On 1 November the accountant prepares the reminders of overdue B2B invoices. French law sets the rate at the ECB rate plus 10 points (2.15 + 10 = 12.15 percent here) on the days late, plus a fixed 40 EUR recovery indemnity per invoice. Invoices not yet due are skipped; every amount is exact.

Pipeline
Script
admin
#1. Unpaid B2B invoices on 2026-11-01, legal rate = ECB rate 2.15 + 10 points;
-> "[today]" "2026-11-01";
-> "[rate]" (bd+ 2.15 10);
json load "rx_ar" "[{\"inv\": \"F-2026-071\", \"client\": \"Batiman SARL\", \"ttc\": \"4860.00\", \"due\": \"2026-07-31\"}, {\"inv\": \"F-2026-088\", \"client\": \"Run Distribution\", \"ttc\": \"1275.60\", \"due\": \"2026-09-15\"}, {\"inv\": \"F-2026-102\", \"client\": \"Pharmacie du Port\", \"ttc\": \"612.00\", \"due\": \"2026-11-10\"}]";
json load "rx_pen" "[]";
json load "rx_tot" "[]";
#2. Days late, exact interest = amount x rate x days / 365, plus the 40 EUR recovery indemnity;
for (-> "[i]" 0) (< [i] (json count "rx_ar" /)) (++ "[i]") {
	-> "[due]" (json select "rx_ar" (concat "/[" [i] "]/due"));
	if (date is_after [today] [due]) {
		-> "[days]" (date nb_day [due] [today]);
		-> "[int]" (bigdec round (bigdec div (bd* (bd* (json select "rx_ar" (concat "/[" [i] "]/ttc")) [rate]) [days]) 36500 20) 2);
		-> "[claim]" (bd+ [int] 40);
		json iarray "rx_tot" "/" [claim] NUM;
		json iarray "rx_pen" "/" (concat "{\"invoice\": \"" (json select "rx_ar" (concat "/[" [i] "]/inv")) "\", \"days_late\": " [days] ", \"interest\": " [int] ", \"claim\": " [claim] "}") OBJ;
	};
};
#3. Total to put on the reminder letters;
-> "[out]" (concat "{\"legal_rate_pct\": " [rate] ", \"penalties\": " (string regexp_replace (json doc "rx_pen") "\s" "") ", \"total_claim\": " (bigdec sum (json doc "rx_tot")) "}");
json unload "rx_ar";
json unload "rx_pen";
json unload "rx_tot";
[out]
Result
mentdb
{"legal_rate_pct": 12.15, "penalties": [{"interest":150.45,"days_late":93,"claim":190.45,"invoice":"F-2026-071"},{"interest":19.96,"days_late":47,"claim":59.96,"invoice":"F-2026-088"}], "total_claim": 250.41}
Go further
  • The rate changes each half-year: read it from a parameter instead of the code
  • Write the reminder letters from the penalties array with mswordx replace
executed on MentDB

Bank reconciliation of account 512

At month end the accountant matches the 512 bank account entries with the bank statement: by reference found in the bank label, then by exact amount. The script lists the matched entries, the entries in the books only (cheque not cashed), the amount gaps (typing error) and the bank lines without a reference (fees), and checks that the balances differ by these items only.

Pipeline
Script
admin
#1. October entries of account 512 and the bank statement lines;
json load "rx_gl" "[{\"ref\": \"VIR-4471\", \"amt\": \"-1250.00\"}, {\"ref\": \"CHQ-0918\", \"amt\": \"-380.40\"}, {\"ref\": \"CLI-2210\", \"amt\": \"2045.15\"}, {\"ref\": \"CB-7781\", \"amt\": \"-64.90\"}, {\"ref\": \"CLI-2214\", \"amt\": \"918.00\"}]";
json load "rx_bk" "[{\"label\": \"VIR SEPA VIR-4471 MARTIN\", \"amt\": \"-1250.00\"}, {\"label\": \"REM CLI-2210 HOARAU\", \"amt\": \"2045.15\"}, {\"label\": \"CB CB-7781 STATION\", \"amt\": \"-64.90\"}, {\"label\": \"FRAIS TENUE COMPTE\", \"amt\": \"-18.50\"}, {\"label\": \"REM CLI-2214 PAYET\", \"amt\": \"981.00\"}]";
json load "rx_rec" "{\"matched\": [], \"in_books_only\": [], \"amount_gap\": [], \"bank_only\": [], \"gl\": [], \"bank\": []}";
#2. Match each ledger entry on its reference in the bank label, then compare the exact amounts;
for (-> "[i]" 0) (< [i] (json count "rx_gl" /)) (++ "[i]") {
	-> "[ref]" (json select "rx_gl" (concat "/[" [i] "]/ref"));
	-> "[amt]" (json select "rx_gl" (concat "/[" [i] "]/amt"));
	json iarray "rx_rec" "/gl" [amt] NUM;
	-> "[found]" -1;
	for (-> "[j]" 0) (< [j] (json count "rx_bk" /)) (++ "[j]") {
		if (string contains (json select "rx_bk" (concat "/[" [j] "]/label")) [ref]) {-> "[found]" [j];};
	};
	if (== [found] -1) {json iarray "rx_rec" "/in_books_only" [ref] STR;} {
		-> "[bamt]" (json select "rx_bk" (concat "/[" [found] "]/amt"));
		if (== (bigdec compare [amt] [bamt]) 0) {json iarray "rx_rec" "/matched" [ref] STR;} {json iarray "rx_rec" "/amount_gap" (concat [ref] " books " [amt] " bank " [bamt] " gap " (bigdec abs (bd- [bamt] [amt]))) STR;};
	};
};
#3. Bank lines without reference in the books: fees, direct debits;
for (-> "[j]" 0) (< [j] (json count "rx_bk" /)) (++ "[j]") {
	json iarray "rx_rec" "/bank" (json select "rx_bk" (concat "/[" [j] "]/amt")) NUM;
	if (not (string regexp (json select "rx_bk" (concat "/[" [j] "]/label")) ".*(VIR|CHQ|CLI|CB)-[0-9]{4}.*")) {json iarray "rx_rec" "/bank_only" (json select "rx_bk" (concat "/[" [j] "]/label")) STR;};
};
#4. Balances must differ only by the unreconciled items;
-> "[gl_bal]" (bigdec sum (json select "rx_rec" "/gl"));
-> "[bk_bal]" (bigdec sum (json select "rx_rec" "/bank"));
json dobject "rx_rec" "/" "gl";
json dobject "rx_rec" "/" "bank";
-> "[out]" (concat "{\"books\": " [gl_bal] ", \"bank\": " [bk_bal] ", \"difference\": " (bd- [bk_bal] [gl_bal]) ", \"detail\": " (string regexp_replace (json doc "rx_rec") "\n *" "") "}");
json unload "rx_gl"; json unload "rx_bk"; json unload "rx_rec";
[out]
Result
mentdb
{"books": 1267.85, "bank": 1692.75, "difference": 424.90, "detail": {"in_books_only": ["CHQ-0918"],"amount_gap": ["CLI-2214 books 918.00 bank 981.00 gap 63.00"],"bank_only": ["FRAIS TENUE COMPTE"],"matched": ["VIR-4471","CLI-2210","CB-7781"]}}
Go further
  • Difference 424.90 = cheque 380.40 + typing gap 63.00 - fees 18.50: the reconciliation is complete
  • Book the fees and the correction, then run it again: the difference must be 0
executed on MentDB

Benford test of expense claims

The auditor checks the first digits of 30 expense claims against the Benford law (log10 of 1 + 1/d). The script counts the first digits, compares them with the expected counts in a chi-square test and counts the claims just below the 5000 EUR approval limit, a classic sign of split or tuned claims.

Pipeline
Script
admin
#1. Expense claims of the year to audit, the approval limit without a second signature is 5000 EUR;
json load "rx_exp" "[1240.50, 186.20, 4870.00, 312.75, 1980.00, 145.90, 4950.00, 2310.40, 118.60, 1460.00, 4795.00, 265.30, 1725.80, 3890.00, 132.40, 4920.00, 1090.00, 618.75, 4880.00, 2745.10, 174.00, 1312.60, 4990.00, 806.40, 1155.20, 4860.00, 3420.00, 97.80, 1570.30, 4935.00]";
json load "rx_dg" "[]"; -> "[digits]" ""; -> "[near]" 0;
json parse_array "rx_exp" "/" "[x]" {
	-> "[digits]" (concat [digits] (string left [x] 1));
	json iarray "rx_dg" "/" (string left [x] 1) INT;
	if (and (>= [x] 4500) (< [x] 5000)) {-> "[near]" (+ [near] 1);};
};
#2. Observed count of each first digit against the Benford law log10 of 1 + 1/d;
json load "rx_obs" "[]"; json load "rx_ben" "[]";
for (-> "[d]" 1) (<= [d] 9) (++ "[d]") {
	json iarray "rx_obs" "/" (string regexp_count [digits] [d]) INT;
	json iarray "rx_ben" "/" (math round (* (math log10 (+ 1 (/ 1 [d]))) 30) 1) NUM;
};
#3. Goodness of fit test, the most frequent first digit and the claims just below the limit;
-> "[out]" (concat "{\"n\": " (json count "rx_exp" "/") ", \"observed_1_to_9\": " (string regexp_replace (json doc "rx_obs") "\s" "") ", \"benford_expected\": " (string regexp_replace (json doc "rx_ben") "\s" "") ", \"mode_digit\": " (stat mode (json doc "rx_dg")) ", \"just_below_5000\": " [near] ", \"test\": " (stat chi2_test (json doc "rx_obs") (json doc "rx_ben")) "}");
json unload "rx_exp"; json unload "rx_dg"; json unload "rx_obs"; json unload "rx_ben";
[out]
Result
mentdb
{"n": 30, "observed_1_to_9": [13,3,3,8,0,1,0,1,1], "benford_expected": [9.0,5.3,3.7,2.9,2.4,2.0,1.7,1.5,1.4], "mode_digit": [1], "just_below_5000": 8, "test": {"chi2": 16.702047178231297, "df": 8, "p_value": 0.03336558198265083}}
Go further
  • p = 0.033: the first digits do not follow Benford, mostly because of eight claims of 4795 to 4990 EUR just below the limit
  • Use stat frequency on the first two digits for a finer test on a large ledger
executed on MentDB

Gaps and duplicates in the invoice numbering

Invoices must be numbered in one continuous sequence without gaps. The script extracts the number of each invoice of the till export, sorts them, finds the duplicated number with the mode and lists the missing numbers between two consecutive invoices.

Pipeline
Script
admin
#1. Sales invoice numbers exported from the till for October, French law requires a continuous sequence;
-> "[list]" "FA2026-0412,FA2026-0415,FA2026-0413,FA2026-0418,FA2026-0414,FA2026-0416,FA2026-0420,FA2026-0416,FA2026-0421,FA2026-0419";
json load "rx_num" "[]";
json load "rx_raw" (string split [list] "," -1);
json parse_array "rx_raw" "/" "[inv]" {
	json iarray "rx_num" "/" (string regexp_replace [inv] "^FA2026-0*" "") INT;
};
#2. Sort the numbers, duplicates are the most frequent values;
-> "[sorted]" (math order_int (json doc "rx_num"));
-> "[dup]" (stat mode [sorted]);
#3. Walk the sorted sequence and list the missing numbers;
json load "rx_s" [sorted]; -> "[gaps]" "";
for (-> "[i]" 1) (< [i] (json count "rx_s" "/")) (++ "[i]") {
	-> "[prev]" (json select "rx_s" (concat "/[" (- [i] 1) "]"));
	for (-> "[k]" (+ [prev] 1)) (< [k] (json select "rx_s" (concat "/[" [i] "]"))) (++ "[k]") {
		-> "[gaps]" (concat [gaps] "FA2026-" (string lpad [k] "0" 4) " ");
	};
};
json unload "rx_num"; json unload "rx_raw"; json unload "rx_s";
concat "sorted: " [sorted] " | duplicated: FA2026-" (string lpad (string regexp_replace [dup] "[^0-9]" "") "0" 4) " | missing: " (string trim [gaps])
Result
mentdb
sorted: [412,413,414,415,416,416,418,419,420,421] | duplicated: FA2026-0416 | missing: FA2026-0417
Go further
  • A missing number must be explained (cancelled invoice kept in the file), a duplicate is a till error to report
  • math order_long does the same for numbers above 2^31, such as a year and sequence joined: 20260000412
executed on MentDB

Aged receivables and doubtful debt allowance

At the closing date the accountant splits the open customer invoices into ageing buckets (not due, 1-30, 31-60, 61-90, over 90 days), computes the allowance for doubtful debts at 0, 10, 25 and 50 percent, the average days late and the age of the oldest invoice in weeks.

Pipeline
Script
admin
#1. Open customer invoices at the closing date 2026-10-31;
-> "[close]" "2026-10-31";
json load "rx_ar" "[{\"client\": \"Batiman\", \"due\": \"2026-10-15\", \"amt\": \"3200.00\"}, {\"client\": \"Run Distribution\", \"due\": \"2026-11-20\", \"amt\": \"1840.50\"}, {\"client\": \"Pharmacie du Port\", \"due\": \"2026-08-31\", \"amt\": \"612.00\"}, {\"client\": \"Hotel Lagon\", \"due\": \"2026-09-10\", \"amt\": \"5420.00\"}, {\"client\": \"Garage Hoarau\", \"due\": \"2026-05-31\", \"amt\": \"980.00\"}, {\"client\": \"Cafe Bourbon\", \"due\": \"2026-10-02\", \"amt\": \"415.80\"}]";
json load "rx_age" "{\"not_due\": 0, \"d1_30\": 0, \"d31_60\": 0, \"d61_90\": 0, \"over_90\": 0}";
json load "rx_days" "[]"; -> "[prov]" 0;
#2. Days past due, bucket and doubtful debt allowance rate of each invoice;
json parse_array "rx_ar" "/" "[inv]" {
	json load "rx_i" [inv];
	-> "[amt]" (json select "rx_i" "/amt");
	-> "[late]" (date nb_day (json select "rx_i" "/due") [close]);
	if (> [late] 0) {json iarray "rx_days" "/" [late] INT;};
	if (<= [late] 0) {-> "[b]" "not_due"; -> "[r]" 0;} {if (<= [late] 30) {-> "[b]" "d1_30"; -> "[r]" 0;} {if (<= [late] 60) {-> "[b]" "d31_60"; -> "[r]" 10;} {if (<= [late] 90) {-> "[b]" "d61_90"; -> "[r]" 25;} {-> "[b]" "over_90"; -> "[r]" 50;};};};};
	json uobject "rx_age" "/" [b] (bd+ (json select "rx_age" (concat "/" [b])) [amt]) NUM;
	-> "[prov]" (bd+ [prov] (bigdec round (bigdec div (bd* [amt] [r]) 100 10) 2));
};
#3. Ageing balance, average days late and weeks of the oldest invoice;
-> "[out]" (concat "{\"ageing\": " (string regexp_replace (json doc "rx_age") "\s" "") ", \"avg_days_late\": " (math round (math avg (json doc "rx_days")) 1) ", \"oldest_weeks\": " (math trunc (/ (stat max (json doc "rx_days")) 7)) ", \"allowance\": " [prov] "}");
json unload "rx_ar"; json unload "rx_i"; json unload "rx_age"; json unload "rx_days";
[out]
Result
mentdb
{"ageing": {"d61_90":612.0,"over_90":980.0,"d1_30":3615.8,"d31_60":5420.0,"not_due":1840.5}, "avg_days_late": 62, "oldest_weeks": 21, "allowance": 1185.00}
Go further
  • Book the allowance in 6817 / 491 and reverse last year's one
  • Weight the days late by the amounts with stat weighted_mean: a 5420 EUR invoice matters more than a 415 EUR one
executed on MentDB

Reciprocal allocation of service department costs

IT and Maintenance serve each other and two production shops. The reciprocal method solves the system IT = 120000 + 10 pct of Maintenance, Maintenance = 80000 + 20 pct of IT, then allocates the full costs to Assembly and Painting. The LU factorisation shows the system is well posed.

Pipeline
Script
admin
#1. Service departments IT and Maintenance serve each other and two production shops;
#IT = 120000 + 10 pct of Maintenance, Maintenance = 80000 + 20 pct of IT;
-> "[A]" "[[1, -0.10], [-0.20, 1]]";
-> "[direct]" "[120000, 80000]";
#2. LU factorisation of the reciprocal system, its determinant is the product of the U diagonal;
json load "rx_lu" (matrix lu [A]);
-> "[det]" (* (json select "rx_lu" "/U/[0]/[0]") (json select "rx_lu" "/U/[1]/[1]"));
#3. Total costs to allocate, then the shares of Assembly and Painting: IT 50 and 30 pct, Maintenance 40 and 50 pct;
-> "[total]" (matrix solve [A] [direct]);
-> "[alloc]" (matrix mul "[[0.50, 0.40], [0.30, 0.50]]" [total]);
json load "rx_al" [alloc];
-> "[out]" (concat "{\"det\": " (math round [det] 4) ", \"total_it_maint\": " [total] ", \"assembly\": " (math round (json select "rx_al" "/[0]") 2) ", \"painting\": " (math round (json select "rx_al" "/[1]") 2) ", \"check_sum\": " (math round (+ (json select "rx_al" "/[0]") (json select "rx_al" "/[1]")) 2) "}");
json unload "rx_lu"; json unload "rx_al";
[out]
Result
mentdb
{"det": 0.98, "total_it_maint": [130612.24489795919,106122.44897959183], "assembly": 107755.1, "painting": 92244.9, "check_sum": 200000}
Go further
  • The direct method ignores the services between IT and Maintenance: compare its result with this one
  • With n service departments the same code works with an n x n matrix
executed on MentDB

Cash rounding of tickets to 5 cents

A shop that does not give 1 and 2 cent coins rounds each cash total to the nearest 0.05, credit notes included. The script keeps the sign of each ticket, rounds its absolute value, shows the cents and books the net rounding difference to the gain (758) or loss (658) account.

Pipeline
Script
admin
#1. Cash sales of a shop that rounds the total to 5 cents, a credit note is negative;
json load "rx_t" "[12.42, 7.98, 23.47, -4.12, 18.03, 9.97, 31.26, -15.48]";
-> "[gain]" 0; -> "[lines]" "";
json parse_array "rx_t" "/" "[x]" {
	#2. Cents part, then nearest 0.05 on the absolute value, the sign of the ticket is kept;
	-> "[cents]" (math round (* (math frac_part [x]) 100) 0);
	-> "[paid]" (* (math sign [x]) (/ (math rint (* (math abs [x]) 20)) 20));
	-> "[diff]" (math round (- [paid] [x]) 2);
	-> "[gain]" (+ [gain] [diff]);
	-> "[lines]" (concat [lines] [x] " pays " (math round [paid] 2) " cents " [cents] ", ");
};
json unload "rx_t";
#3. The rounding differences go to 758 when positive, 658 when negative;
-> "[g]" (math round [gain] 2);
concat [lines] "net rounding " [g] " EUR to account " (if (>= (math signum [g]) 0) {758;} {658;})
Result
mentdb
12.42 pays 12.4 cents 42, 7.98 pays 8 cents 98, 23.47 pays 23.45 cents 47, -4.12 pays -4.1 cents -12, 18.03 pays 18.05 cents 3, 9.97 pays 9.95 cents 97, 31.26 pays 31.25 cents 26, -15.48 pays -15.5 cents -48, net rounding -0.03 EUR to account 658
Go further
  • math rint rounds a tie such as 2.5 to the even integer 2: for a ticket ending in 0.025 the shop must choose its rule
  • Card payments are not rounded: apply this only to the cash tickets

Also in Business & IT

FA
Financial analyst
Loan schedules, NPV and IRR, portfolio optimisation: solve and minimise in one line.
10 recipes
AR
Actuary & risk analyst
Frequency and severity laws, simulated losses, quantiles and risk capital.
10 recipes
HP
HR & payroll
Ages, seniority, leave balances and salary statistics.
10 recipes
LS
Logistics & supply chain
Order quantities, safety stock, reorder points and delivery dates.
10 recipes
SM
Sales & marketing analyst
Growth trends, seasonality, A/B tests and forecasts.
10 recipes
WA
Web & API developer
Build JSON responses, encode URLs and headers, parse logs with regex.
10 recipes
SE
Security engineer
Password hashing, webhook signatures, password strength and entropy.
10 recipes
TL
Text & language processing
Count words, match misspellings, convert names between case conventions.
10 recipes