@purveyors/cli 0.2.1 → 0.3.0

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.
Files changed (73) hide show
  1. package/dist/commands/catalog.d.ts +4 -56
  2. package/dist/commands/catalog.d.ts.map +1 -1
  3. package/dist/commands/catalog.js +14 -81
  4. package/dist/commands/catalog.js.map +1 -1
  5. package/dist/commands/inventory.d.ts +2 -25
  6. package/dist/commands/inventory.d.ts.map +1 -1
  7. package/dist/commands/inventory.js +47 -147
  8. package/dist/commands/inventory.js.map +1 -1
  9. package/dist/commands/roast.d.ts +2 -41
  10. package/dist/commands/roast.d.ts.map +1 -1
  11. package/dist/commands/roast.js +138 -125
  12. package/dist/commands/roast.js.map +1 -1
  13. package/dist/commands/sales.d.ts +2 -10
  14. package/dist/commands/sales.d.ts.map +1 -1
  15. package/dist/commands/sales.js +30 -109
  16. package/dist/commands/sales.js.map +1 -1
  17. package/dist/commands/tasting.d.ts +4 -38
  18. package/dist/commands/tasting.d.ts.map +1 -1
  19. package/dist/commands/tasting.js +18 -108
  20. package/dist/commands/tasting.js.map +1 -1
  21. package/dist/lib/artisan/db.d.ts +37 -0
  22. package/dist/lib/artisan/db.d.ts.map +1 -0
  23. package/dist/lib/artisan/db.js +51 -0
  24. package/dist/lib/artisan/db.js.map +1 -0
  25. package/dist/lib/artisan/import.d.ts +16 -0
  26. package/dist/lib/artisan/import.d.ts.map +1 -0
  27. package/dist/lib/artisan/import.js +447 -0
  28. package/dist/lib/artisan/import.js.map +1 -0
  29. package/dist/lib/artisan/index.d.ts +9 -0
  30. package/dist/lib/artisan/index.d.ts.map +1 -0
  31. package/dist/lib/artisan/index.js +7 -0
  32. package/dist/lib/artisan/index.js.map +1 -0
  33. package/dist/lib/artisan/parser.d.ts +19 -0
  34. package/dist/lib/artisan/parser.d.ts.map +1 -0
  35. package/dist/lib/artisan/parser.js +376 -0
  36. package/dist/lib/artisan/parser.js.map +1 -0
  37. package/dist/lib/artisan/temperature.d.ts +52 -0
  38. package/dist/lib/artisan/temperature.d.ts.map +1 -0
  39. package/dist/lib/artisan/temperature.js +101 -0
  40. package/dist/lib/artisan/temperature.js.map +1 -0
  41. package/dist/lib/artisan/types.d.ts +195 -0
  42. package/dist/lib/artisan/types.d.ts.map +1 -0
  43. package/dist/lib/artisan/types.js +35 -0
  44. package/dist/lib/artisan/types.js.map +1 -0
  45. package/dist/lib/artisan/validator.d.ts +14 -0
  46. package/dist/lib/artisan/validator.d.ts.map +1 -0
  47. package/dist/lib/artisan/validator.js +228 -0
  48. package/dist/lib/artisan/validator.js.map +1 -0
  49. package/dist/lib/catalog.d.ts +87 -0
  50. package/dist/lib/catalog.d.ts.map +1 -0
  51. package/dist/lib/catalog.js +111 -0
  52. package/dist/lib/catalog.js.map +1 -0
  53. package/dist/lib/index.d.ts +6 -0
  54. package/dist/lib/index.d.ts.map +1 -0
  55. package/dist/lib/index.js +11 -0
  56. package/dist/lib/index.js.map +1 -0
  57. package/dist/lib/inventory.d.ts +80 -0
  58. package/dist/lib/inventory.d.ts.map +1 -0
  59. package/dist/lib/inventory.js +205 -0
  60. package/dist/lib/inventory.js.map +1 -0
  61. package/dist/lib/roast.d.ts +127 -0
  62. package/dist/lib/roast.d.ts.map +1 -0
  63. package/dist/lib/roast.js +284 -0
  64. package/dist/lib/roast.js.map +1 -0
  65. package/dist/lib/sales.d.ts +53 -0
  66. package/dist/lib/sales.d.ts.map +1 -0
  67. package/dist/lib/sales.js +155 -0
  68. package/dist/lib/sales.js.map +1 -0
  69. package/dist/lib/tasting.d.ts +76 -0
  70. package/dist/lib/tasting.d.ts.map +1 -0
  71. package/dist/lib/tasting.js +136 -0
  72. package/dist/lib/tasting.js.map +1 -0
  73. package/package.json +13 -2
@@ -1,8 +1,11 @@
1
1
  import { Command } from 'commander';
2
+ import { access, readFile } from 'fs/promises';
3
+ import { basename } from 'path';
2
4
  import { createAuthenticatedClient } from '../lib/supabase.js';
3
5
  import { outputData, info, success } from '../lib/output.js';
4
6
  import { withErrorHandling, AuthError, PrvrsError } from '../lib/errors.js';
5
7
  import { confirm, todayIso } from '../lib/prompts.js';
8
+ import { listRoasts, getRoast, createRoast, deleteRoast, importRoastFromFile, } from '../lib/roast.js';
6
9
  // ─── Command builder ──────────────────────────────────────────────────────────
7
10
  /**
8
11
  * `purvey roast` — Browse and manage your roast profiles.
@@ -23,18 +26,11 @@ export function buildRoastCommand() {
23
26
  if (!user) {
24
27
  throw new AuthError('Not logged in. Run `purvey auth login` first.');
25
28
  }
26
- let query = supabase
27
- .from('roast_profiles')
28
- .select('roast_id, batch_name, coffee_id, coffee_name, roast_date, oz_in, oz_out, weight_loss_percent, roast_notes, roaster_type, roaster_size, temperature_unit, total_roast_time, development_percent, data_source, last_updated')
29
- .eq('user', user.id);
30
- if (opts.coffeeId !== undefined) {
31
- query = query.eq('coffee_id', parseInt(opts.coffeeId, 10));
32
- }
33
- const limit = Math.max(1, parseInt(opts.limit, 10));
34
- const { data, error } = await query.order('roast_date', { ascending: false }).limit(limit);
35
- if (error)
36
- throw error;
37
- if (!data || data.length === 0) {
29
+ const data = await listRoasts(supabase, user.id, {
30
+ coffeeId: opts.coffeeId !== undefined ? parseInt(opts.coffeeId, 10) : undefined,
31
+ limit: Math.max(1, parseInt(opts.limit, 10)),
32
+ });
33
+ if (data.length === 0) {
38
34
  info('No roast profiles found.');
39
35
  return;
40
36
  }
@@ -53,44 +49,11 @@ export function buildRoastCommand() {
53
49
  if (!user) {
54
50
  throw new AuthError('Not logged in. Run `purvey auth login` first.');
55
51
  }
56
- const roastId = parseInt(id, 10);
57
- // Fetch the profile, verifying ownership
58
- const { data: profile, error: profileError } = await supabase
59
- .from('roast_profiles')
60
- .select('*')
61
- .eq('roast_id', roastId)
62
- .eq('user', user.id)
63
- .single();
64
- if (profileError) {
65
- if (profileError.code === 'PGRST116') {
66
- throw new AuthError(`Roast profile ${id} not found or does not belong to you.`);
67
- }
68
- throw profileError;
69
- }
70
- const result = { ...profile };
71
- // Optionally fetch temperature curve
72
- if (opts.includeTemps) {
73
- const { data: temps, error: tempError } = await supabase
74
- .from('roast_temperatures')
75
- .select('roast_id, time_seconds, bean_temp, environmental_temp')
76
- .eq('roast_id', roastId)
77
- .order('time_seconds', { ascending: true });
78
- if (tempError)
79
- throw tempError;
80
- result.temperatures = temps ?? [];
81
- }
82
- // Optionally fetch roast events
83
- if (opts.includeEvents) {
84
- const { data: events, error: eventsError } = await supabase
85
- .from('roast_events')
86
- .select('roast_id, time_seconds, event_type, event_value')
87
- .eq('roast_id', roastId)
88
- .order('time_seconds', { ascending: true });
89
- if (eventsError)
90
- throw eventsError;
91
- result.events = events ?? [];
92
- }
93
- outputData(result, globalOpts);
52
+ const data = await getRoast(supabase, user.id, parseInt(id, 10), {
53
+ includeTemps: Boolean(opts.includeTemps),
54
+ includeEvents: Boolean(opts.includeEvents),
55
+ });
56
+ outputData(data, globalOpts);
94
57
  }));
95
58
  // ── roast create ──────────────────────────────────────────────────────────
96
59
  roast
@@ -113,62 +76,27 @@ export function buildRoastCommand() {
113
76
  if (isNaN(coffeeId)) {
114
77
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid --coffee-id: "${opts.coffeeId}".`);
115
78
  }
116
- // Verify ownership of the inventory item and get coffee name for default batch name
117
- const { data: invItem, error: invError } = await supabase
118
- .from('green_coffee_inv')
119
- .select('id, coffee_catalog!catalog_id (name)')
120
- .eq('id', coffeeId)
121
- .eq('user', user.id)
122
- .single();
123
- if (invError || !invItem) {
124
- throw new AuthError(`Inventory item ${coffeeId} not found or does not belong to you.`);
125
- }
126
- const roastDate = opts.roastDate ?? todayIso();
127
- // Default batch name: coffee name + roast date
128
- let batchName = opts.batchName;
129
- if (!batchName) {
130
- const catalogRaw = invItem.coffee_catalog;
131
- const catalog = Array.isArray(catalogRaw) ? (catalogRaw[0] ?? null) : catalogRaw;
132
- const coffeeName = catalog?.name ?? `Coffee #${coffeeId}`;
133
- batchName = `${coffeeName} — ${roastDate}`;
134
- }
135
- const insertPayload = {
136
- user: user.id,
137
- coffee_id: coffeeId,
138
- batch_name: batchName,
139
- roast_date: roastDate,
140
- };
79
+ let ozIn;
141
80
  if (opts.ozIn !== undefined) {
142
- const ozIn = parseFloat(opts.ozIn);
81
+ ozIn = parseFloat(opts.ozIn);
143
82
  if (isNaN(ozIn) || ozIn <= 0)
144
83
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid --oz-in: "${opts.ozIn}".`);
145
- insertPayload.oz_in = ozIn;
146
84
  }
85
+ let ozOut;
147
86
  if (opts.ozOut !== undefined) {
148
- const ozOut = parseFloat(opts.ozOut);
87
+ ozOut = parseFloat(opts.ozOut);
149
88
  if (isNaN(ozOut) || ozOut <= 0)
150
89
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid --oz-out: "${opts.ozOut}".`);
151
- insertPayload.oz_out = ozOut;
152
- }
153
- if (opts.notes !== undefined) {
154
- insertPayload.roast_notes = opts.notes;
155
- }
156
- const { data: inserted, error: insertError } = await supabase
157
- .from('roast_profiles')
158
- .insert(insertPayload)
159
- .select('roast_id')
160
- .single();
161
- if (insertError)
162
- throw insertError;
163
- // Re-fetch the full row
164
- const { data, error } = await supabase
165
- .from('roast_profiles')
166
- .select('roast_id, batch_name, coffee_id, coffee_name, roast_date, oz_in, oz_out, weight_loss_percent, roast_notes, roaster_type, total_roast_time, data_source, last_updated')
167
- .eq('roast_id', inserted.roast_id)
168
- .single();
169
- if (error)
170
- throw error;
171
- success(`Roast profile ${inserted.roast_id} created.`);
90
+ }
91
+ const data = await createRoast(supabase, user.id, {
92
+ coffeeId,
93
+ batchName: opts.batchName,
94
+ ozIn,
95
+ ozOut,
96
+ roastDate: opts.roastDate ?? todayIso(),
97
+ notes: opts.notes,
98
+ });
99
+ success(`Roast profile ${data.roast_id} created.`);
172
100
  outputData(data, globalOpts);
173
101
  }));
174
102
  // ── roast delete <id> ─────────────────────────────────────────────────────
@@ -177,7 +105,7 @@ export function buildRoastCommand() {
177
105
  .description('Delete a roast profile (must be yours)')
178
106
  .option('-y, --yes', 'Skip confirmation prompt')
179
107
  .action(withErrorHandling(async (id, opts, cmd) => {
180
- void cmd; // global opts not needed for delete
108
+ void cmd;
181
109
  const supabase = await createAuthenticatedClient();
182
110
  const { data: { user }, } = await supabase.auth.getUser();
183
111
  if (!user) {
@@ -187,39 +115,124 @@ export function buildRoastCommand() {
187
115
  if (isNaN(roastId)) {
188
116
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid roast ID: "${id}".`);
189
117
  }
190
- // Verify ownership
191
- const { data: existing, error: fetchError } = await supabase
192
- .from('roast_profiles')
193
- .select('roast_id, batch_name')
194
- .eq('roast_id', roastId)
195
- .eq('user', user.id)
196
- .single();
197
- if (fetchError || !existing) {
198
- throw new AuthError(`Roast profile ${id} not found or does not belong to you.`);
199
- }
200
118
  if (!opts.yes) {
201
- const label = existing.batch_name ? `"${existing.batch_name}"` : `#${roastId}`;
202
- const ok = await confirm(`Delete roast profile ${label}?`);
119
+ const ok = await confirm(`Delete roast profile #${roastId}?`);
203
120
  if (!ok) {
204
121
  info('Aborted.');
205
122
  return;
206
123
  }
207
124
  }
208
- const { error: deleteError } = await supabase
209
- .from('roast_profiles')
210
- .delete()
211
- .eq('roast_id', roastId)
212
- .eq('user', user.id);
213
- if (deleteError)
214
- throw deleteError;
125
+ await deleteRoast(supabase, user.id, roastId);
215
126
  success(`Roast profile ${roastId} deleted.`);
216
127
  }));
217
- // ── roast import-artisan <id> <file.alog> ─────────────────────────────────
218
- // TODO (Phase 3): Wire up artisan import.
219
- // The .alog parser runs server-side on the SvelteKit API. This command needs
220
- // to POST the file content to the app's /api/artisan-import endpoint, which
221
- // requires the SvelteKit server URL to be configurable (not just Supabase URL).
222
- // Tracked for Phase 3 implementation.
128
+ // ── roast import <file> ───────────────────────────────────────────────────
129
+ roast
130
+ .command('import <file>')
131
+ .description('Import an Artisan .alog file and create a new roast profile')
132
+ .requiredOption('--coffee-id <id>', 'green_coffee_inv ID for this roast')
133
+ .option('--batch-name <name>', 'Batch name (auto-generated from coffee name + date if omitted)')
134
+ .option('--oz-in <oz>', 'Green weight in ounces (extracted from .alog if omitted)')
135
+ .option('--roast-notes <notes>', 'Additional roast notes')
136
+ .action(withErrorHandling(async (file, opts, cmd) => {
137
+ const globalOpts = cmd.optsWithGlobals();
138
+ // 1. Validate file exists and is readable
139
+ try {
140
+ await access(file);
141
+ }
142
+ catch {
143
+ throw new PrvrsError('INVALID_ARGUMENT', `File not found or not readable: "${file}"`);
144
+ }
145
+ // 2. Read file content
146
+ const fileContent = await readFile(file, 'utf-8');
147
+ const fileName = basename(file);
148
+ // 3. Authenticate
149
+ const supabase = await createAuthenticatedClient();
150
+ const { data: { user }, } = await supabase.auth.getUser();
151
+ if (!user) {
152
+ throw new AuthError('Not logged in. Run `purvey auth login` first.');
153
+ }
154
+ // 4. Parse --coffee-id
155
+ const coffeeId = parseInt(opts.coffeeId, 10);
156
+ if (isNaN(coffeeId) || coffeeId <= 0) {
157
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid --coffee-id: "${opts.coffeeId}".`);
158
+ }
159
+ // 5. Parse --oz-in if provided
160
+ let ozIn;
161
+ if (opts.ozIn !== undefined) {
162
+ ozIn = parseFloat(opts.ozIn);
163
+ if (isNaN(ozIn) || ozIn <= 0) {
164
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid --oz-in: "${opts.ozIn}".`);
165
+ }
166
+ }
167
+ // 6. Run the import
168
+ const result = await importRoastFromFile(supabase, user.id, {
169
+ fileContent,
170
+ fileName,
171
+ coffeeId,
172
+ batchName: opts.batchName,
173
+ ozIn,
174
+ roastNotes: opts.roastNotes,
175
+ });
176
+ // 7. Output
177
+ if (globalOpts.pretty) {
178
+ printImportPretty(result, coffeeId);
179
+ }
180
+ else {
181
+ success(`Roast profile ${result.roast_id} imported from ${fileName}.`);
182
+ outputData(result, globalOpts);
183
+ }
184
+ }));
223
185
  return roast;
224
186
  }
187
+ // ─── Pretty-print helper ──────────────────────────────────────────────────────
188
+ function formatTime(seconds) {
189
+ const m = Math.floor(seconds / 60);
190
+ const s = Math.round(seconds % 60);
191
+ return `${m}:${s.toString().padStart(2, '0')}`;
192
+ }
193
+ function printImportPretty(result, coffeeId) {
194
+ const { milestones, phases } = result;
195
+ console.log('');
196
+ console.log('✓ Roast imported successfully');
197
+ console.log('');
198
+ console.log(` Roast ID: ${result.roast_id}`);
199
+ console.log(` Bean: ${result.coffee_name} (#${coffeeId})`);
200
+ console.log(` Batch: ${result.batch_name}`);
201
+ const tempCount = result.message.match(/(\d+) data points/)?.[1] ?? '?';
202
+ console.log(` Data points: ${tempCount} temperatures, ${result.milestone_events} milestones, ${result.control_events} control events`);
203
+ // Milestones
204
+ if (Object.keys(milestones).length > 0) {
205
+ console.log('');
206
+ console.log(' Milestones:');
207
+ const labels = {
208
+ charge: 'Charge',
209
+ dry_end: 'Dry End',
210
+ fc_start: 'FC Start',
211
+ fc_end: 'FC End',
212
+ sc_start: 'SC Start',
213
+ drop: 'Drop',
214
+ cool: 'Cool',
215
+ };
216
+ for (const [key, label] of Object.entries(labels)) {
217
+ const t = milestones[key];
218
+ if (t !== undefined && t > 0) {
219
+ console.log(` ${label.padEnd(10)}${formatTime(t)}`);
220
+ }
221
+ }
222
+ }
223
+ // Phases
224
+ const { drying_percent, maillard_percent, development_percent, total_time_seconds } = phases;
225
+ if (total_time_seconds > 0) {
226
+ console.log('');
227
+ console.log(' Phases:');
228
+ if (drying_percent > 0)
229
+ console.log(` Drying ${Math.round(drying_percent)}%`);
230
+ if (maillard_percent > 0)
231
+ console.log(` Maillard ${Math.round(maillard_percent)}%`);
232
+ if (development_percent > 0)
233
+ console.log(` Development ${Math.round(development_percent)}%`);
234
+ console.log(` Total ${formatTime(total_time_seconds)}`);
235
+ }
236
+ console.log('');
237
+ }
225
238
  //# sourceMappingURL=roast.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"roast.js","sourceRoot":"","sources":["../../src/commands/roast.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"roast.js","sourceRoot":"","sources":["../../src/commands/roast.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,6 @@
1
1
  import { Command } from 'commander';
2
- export interface Sale {
3
- id: number;
4
- roast_id: number | null;
5
- oz_sold: number | null;
6
- sale_price: number | null;
7
- buyer: string | null;
8
- sell_date: string | null;
9
- user: string;
10
- last_updated: string;
11
- }
2
+ import type { Sale } from '../lib/sales.js';
3
+ export type { Sale };
12
4
  /**
13
5
  * `purvey sales` — Record and manage coffee sales.
14
6
  * Requires authentication.
@@ -1 +1 @@
1
- {"version":3,"file":"sales.d.ts","sourceRoot":"","sources":["../../src/commands/sales.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CA0R3C"}
1
+ {"version":3,"file":"sales.d.ts","sourceRoot":"","sources":["../../src/commands/sales.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAI5C,YAAY,EAAE,IAAI,EAAE,CAAC;AAIrB;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CA+L3C"}
@@ -3,8 +3,7 @@ import { createAuthenticatedClient } from '../lib/supabase.js';
3
3
  import { outputData, info, success } from '../lib/output.js';
4
4
  import { withErrorHandling, AuthError, PrvrsError } from '../lib/errors.js';
5
5
  import { confirm, todayIso } from '../lib/prompts.js';
6
- // ─── Shared select columns ────────────────────────────────────────────────────
7
- const SALE_SELECT = 'id, roast_id, oz_sold, sale_price, buyer, sell_date, user, last_updated';
6
+ import { listSales, recordSale, updateSale, deleteSale } from '../lib/sales.js';
8
7
  // ─── Command builder ──────────────────────────────────────────────────────────
9
8
  /**
10
9
  * `purvey sales` — Record and manage coffee sales.
@@ -24,17 +23,10 @@ export function buildSalesCommand() {
24
23
  if (!user) {
25
24
  throw new AuthError('Not logged in. Run `purvey auth login` first.');
26
25
  }
27
- const limit = Math.max(1, parseInt(opts.limit, 10));
28
- // Join with roast_profiles to surface coffee name
29
- const { data, error } = await supabase
30
- .from('sales')
31
- .select(`${SALE_SELECT}, roast_profiles!roast_id (batch_name, coffee_name)`)
32
- .eq('user', user.id)
33
- .order('sell_date', { ascending: false })
34
- .limit(limit);
35
- if (error)
36
- throw error;
37
- if (!data || data.length === 0) {
26
+ const data = await listSales(supabase, user.id, {
27
+ limit: Math.max(1, parseInt(opts.limit, 10)),
28
+ });
29
+ if (data.length === 0) {
38
30
  info('No sales found.');
39
31
  return;
40
32
  }
@@ -68,42 +60,14 @@ export function buildSalesCommand() {
68
60
  if (isNaN(price) || price < 0) {
69
61
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid --price: "${opts.price}". Must be a non-negative number.`);
70
62
  }
71
- // Verify the roast profile belongs to the user
72
- const { data: roastExists, error: roastError } = await supabase
73
- .from('roast_profiles')
74
- .select('roast_id')
75
- .eq('roast_id', roastId)
76
- .eq('user', user.id)
77
- .single();
78
- if (roastError || !roastExists) {
79
- throw new AuthError(`Roast profile ${roastId} not found or does not belong to you.`);
80
- }
81
- const insertPayload = {
82
- user: user.id,
83
- roast_id: roastId,
84
- oz_sold: oz,
85
- sale_price: price,
86
- sell_date: opts.sellDate ?? todayIso(),
87
- };
88
- if (opts.buyer !== undefined) {
89
- insertPayload.buyer = opts.buyer;
90
- }
91
- const { data: inserted, error: insertError } = await supabase
92
- .from('sales')
93
- .insert(insertPayload)
94
- .select('id')
95
- .single();
96
- if (insertError)
97
- throw insertError;
98
- // Re-fetch the full row with roast join
99
- const { data, error } = await supabase
100
- .from('sales')
101
- .select(`${SALE_SELECT}, roast_profiles!roast_id (batch_name, coffee_name)`)
102
- .eq('id', inserted.id)
103
- .single();
104
- if (error)
105
- throw error;
106
- success(`Sale ${inserted.id} recorded.`);
63
+ const data = await recordSale(supabase, user.id, {
64
+ roastId,
65
+ oz,
66
+ price,
67
+ buyer: opts.buyer,
68
+ sellDate: opts.sellDate ?? todayIso(),
69
+ });
70
+ success(`Sale ${data.id} recorded.`);
107
71
  outputData(data, globalOpts);
108
72
  }));
109
73
  // ── sales update <id> ─────────────────────────────────────────────────────
@@ -125,54 +89,30 @@ export function buildSalesCommand() {
125
89
  if (isNaN(saleId)) {
126
90
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid sale ID: "${id}".`);
127
91
  }
128
- // Verify ownership
129
- const { data: existing, error: fetchError } = await supabase
130
- .from('sales')
131
- .select('id')
132
- .eq('id', saleId)
133
- .eq('user', user.id)
134
- .single();
135
- if (fetchError || !existing) {
136
- throw new AuthError(`Sale ${id} not found or does not belong to you.`);
137
- }
138
- // Build partial update
139
- const updates = {};
92
+ let oz;
140
93
  if (opts.oz !== undefined) {
141
- const oz = parseFloat(opts.oz);
94
+ oz = parseFloat(opts.oz);
142
95
  if (isNaN(oz) || oz <= 0)
143
96
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid --oz: "${opts.oz}".`);
144
- updates.oz_sold = oz;
145
97
  }
98
+ let price;
146
99
  if (opts.price !== undefined) {
147
- const price = parseFloat(opts.price);
100
+ price = parseFloat(opts.price);
148
101
  if (isNaN(price) || price < 0)
149
102
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid --price: "${opts.price}".`);
150
- updates.sale_price = price;
151
103
  }
152
- if (opts.buyer !== undefined) {
153
- updates.buyer = opts.buyer;
154
- }
155
- if (opts.sellDate !== undefined) {
156
- updates.sell_date = opts.sellDate;
157
- }
158
- if (Object.keys(updates).length === 0) {
104
+ if (oz === undefined &&
105
+ price === undefined &&
106
+ opts.buyer === undefined &&
107
+ opts.sellDate === undefined) {
159
108
  throw new PrvrsError('INVALID_ARGUMENT', 'No update fields provided. Pass at least one of: --oz, --price, --buyer, --sell-date.');
160
109
  }
161
- const { error: updateError } = await supabase
162
- .from('sales')
163
- .update(updates)
164
- .eq('id', saleId)
165
- .eq('user', user.id);
166
- if (updateError)
167
- throw updateError;
168
- // Re-fetch the updated row
169
- const { data, error } = await supabase
170
- .from('sales')
171
- .select(`${SALE_SELECT}, roast_profiles!roast_id (batch_name, coffee_name)`)
172
- .eq('id', saleId)
173
- .single();
174
- if (error)
175
- throw error;
110
+ const data = await updateSale(supabase, user.id, saleId, {
111
+ oz,
112
+ price,
113
+ buyer: opts.buyer,
114
+ sellDate: opts.sellDate,
115
+ });
176
116
  success(`Sale ${saleId} updated.`);
177
117
  outputData(data, globalOpts);
178
118
  }));
@@ -182,7 +122,7 @@ export function buildSalesCommand() {
182
122
  .description('Delete a sale (must be yours)')
183
123
  .option('-y, --yes', 'Skip confirmation prompt')
184
124
  .action(withErrorHandling(async (id, opts, cmd) => {
185
- void cmd; // global opts not needed for delete
125
+ void cmd;
186
126
  const supabase = await createAuthenticatedClient();
187
127
  const { data: { user }, } = await supabase.auth.getUser();
188
128
  if (!user) {
@@ -192,33 +132,14 @@ export function buildSalesCommand() {
192
132
  if (isNaN(saleId)) {
193
133
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid sale ID: "${id}".`);
194
134
  }
195
- // Verify ownership
196
- const { data: existing, error: fetchError } = await supabase
197
- .from('sales')
198
- .select('id, sell_date, oz_sold')
199
- .eq('id', saleId)
200
- .eq('user', user.id)
201
- .single();
202
- if (fetchError || !existing) {
203
- throw new AuthError(`Sale ${id} not found or does not belong to you.`);
204
- }
205
135
  if (!opts.yes) {
206
- const label = existing.sell_date
207
- ? `from ${existing.sell_date} (${existing.oz_sold} oz)`
208
- : `#${saleId}`;
209
- const ok = await confirm(`Delete sale ${label}?`);
136
+ const ok = await confirm(`Delete sale #${saleId}?`);
210
137
  if (!ok) {
211
138
  info('Aborted.');
212
139
  return;
213
140
  }
214
141
  }
215
- const { error: deleteError } = await supabase
216
- .from('sales')
217
- .delete()
218
- .eq('id', saleId)
219
- .eq('user', user.id);
220
- if (deleteError)
221
- throw deleteError;
142
+ await deleteSale(supabase, user.id, saleId);
222
143
  success(`Sale ${saleId} deleted.`);
223
144
  }));
224
145
  return sales;