Skip to content

Commit

Permalink
adding ability to backfill invoices for a user as-needed.
Browse files Browse the repository at this point in the history
  • Loading branch information
tomgobich committed Oct 24, 2023
1 parent 1cb359e commit cebff47
Show file tree
Hide file tree
Showing 11 changed files with 226 additions and 19 deletions.
6 changes: 5 additions & 1 deletion app/Controllers/Http/AssetsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import AssetService from 'App/Services/AssetService';
import CacheService from 'App/Services/CacheService';
import Database from '@ioc:Adonis/Lucid/Database';
import Drive from '@ioc:Adonis/Core/Drive'
import Application from '@ioc:Adonis/Core/Application';

export default class AssetsController {
public async index({ }: HttpContextContract) {
Expand Down Expand Up @@ -124,7 +125,10 @@ export default class AssetsController {
await asset.related('taxonomies').query().update({ assetId: null })
await asset.delete()

await Drive.delete(asset.filename)
// don't actually delete the file when not in production
if (Application.inProduction) {
await Drive.delete(asset.filename)
}

return response.status(200).json({
status: 200,
Expand Down
32 changes: 32 additions & 0 deletions app/Controllers/Http/InvoicesController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User'
import InvoiceStoreValidator from 'App/Validators/InvoiceStoreValidator'
import { DateTime } from 'luxon'

export default class InvoicesController {
public async index({}: HttpContextContract) {}

public async create({}: HttpContextContract) {}

public async store({ request, response, params }: HttpContextContract) {
const { periodStartAt, periodEndAt, ...data } = await request.validate(InvoiceStoreValidator)
const user = await User.findOrFail(params.id)

await user.related('invoices').create({
...data,
paid: true,
periodStartAt: periodStartAt ? DateTime.fromSeconds(periodStartAt) : undefined,
periodEndAt: periodEndAt ? DateTime.fromSeconds(periodEndAt) : undefined
})

return response.redirect().back()
}

public async show({}: HttpContextContract) {}

public async edit({}: HttpContextContract) {}

public async update({}: HttpContextContract) {}

public async destroy({}: HttpContextContract) {}
}
2 changes: 1 addition & 1 deletion app/Controllers/Http/PostsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export default class PostsController {

const postTypes = PostType
const taxonomies = await TaxonomyService.getAllForTree()

return view.render('studio/posts/createOrEdit', { post, assets, taxonomies, postTypes })
}

Expand Down
3 changes: 2 additions & 1 deletion app/Controllers/Http/UsersController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ export default class UsersController {
const roles = await Role.all()
const user = await User.findOrFail(params.id)
const profile = await user.related('profile').query().firstOrFail()
const invoices = await user.related('invoices').query().orderBy('updatedAt', 'desc')

return view.render('studio/users/show', { roles, user, profile })
return view.render('studio/users/show', { roles, user, profile, invoices })
}

public async edit({}: HttpContextContract) {}
Expand Down
50 changes: 50 additions & 0 deletions app/Models/Invoice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { DateTime } from 'luxon'
import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm'
import User from './User'

export default class Invoice extends BaseModel {
@column({ isPrimary: true })
public id: number

@column()
public userId: number

@column()
public invoiceId: string

@column()
public invoiceNumber: string

@column()
public chargeId: string

@column()
public amountDue: number

@column()
public amountPaid: number

@column()
public amountRemaining: number

@column()
public status: string

@column()
public paid: boolean

@column.dateTime()
public periodStartAt: DateTime

@column.dateTime()
public periodEndAt: DateTime

@column.dateTime({ autoCreate: true })
public createdAt: DateTime

@column.dateTime({ autoCreate: true, autoUpdate: true })
public updatedAt: DateTime

@belongsTo(() => User)
public user: BelongsTo<typeof User>
}
4 changes: 4 additions & 0 deletions app/Models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import RequestVote from './RequestVote'
import HistoryTypes from 'App/Enums/HistoryTypes'
import Plan from './Plan'
import Plans from 'App/Enums/Plans'
import Invoice from './Invoice'

class User extends AppBaseModel {
@column({ isPrimary: true })
Expand Down Expand Up @@ -205,6 +206,9 @@ class User extends AppBaseModel {
onQuery: query => query.whereNotNull('lessonRequestId')
})
public lessonRequestVotes: HasMany<typeof RequestVote>

@hasMany(() => Invoice)
public invoices: HasMany<typeof Invoice>
}

User['findForAuth'] = function (uids: string[], uidValue: string) {
Expand Down
50 changes: 50 additions & 0 deletions app/Validators/InvoiceStoreValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { schema, rules } from '@ioc:Adonis/Core/Validator'
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class InvoiceStoreValidator {
constructor(protected ctx: HttpContextContract) {}

/*
* Define schema to validate the "shape", "type", "formatting" and "integrity" of data.
*
* For example:
* 1. The username must be of data type string. But then also, it should
* not contain special characters or numbers.
* ```
* schema.string({}, [ rules.alpha() ])
* ```
*
* 2. The email must be of data type string, formatted as a valid
* email. But also, not used by any other user.
* ```
* schema.string({}, [
* rules.email(),
* rules.unique({ table: 'users', column: 'email' }),
* ])
* ```
*/
public schema = schema.create({
invoiceId: schema.string([rules.unique({ table: 'invoices', column: 'invoice_id' })]),
invoiceNumber: schema.string(),
chargeId: schema.string.optional(),
amountDue: schema.number(),
amountPaid: schema.number(),
amountRemaining: schema.number(),
status: schema.string.optional(),
periodStartAt: schema.number.optional(),
periodEndAt: schema.number.optional(),
})

/**
* Custom messages for validation failures. You can make use of dot notation `(.)`
* for targeting nested fields and array expressions `(*)` for targeting all
* children of an array. For example:
*
* {
* 'profile.username.required': 'Username is required',
* 'scores.*.number': 'Define scores as valid numbers'
* }
*
*/
public messages = {}
}
16 changes: 8 additions & 8 deletions public/assets/entrypoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@
"entrypoints": {
"app": {
"css": [
"http://localhost:64876/assets/app.css"
"http://localhost:63685/assets/app.css"
],
"js": [
"http://localhost:64876/assets/app.js"
"http://localhost:63685/assets/app.js"
]
},
"post": {
"js": [
"http://localhost:64876/assets/post.js"
"http://localhost:63685/assets/post.js"
]
},
"stream": {
"js": [
"http://localhost:64876/assets/stream.js"
"http://localhost:63685/assets/stream.js"
]
},
"file_manager": {
"js": [
"http://localhost:64876/assets/file_manager.js"
"http://localhost:63685/assets/file_manager.js"
]
},
"tiptap_basic": {
"js": [
"http://localhost:64876/assets/tiptap_basic.js"
"http://localhost:63685/assets/tiptap_basic.js"
]
},
"studio.posts.editor": {
"js": [
"http://localhost:64876/assets/studio.posts.editor.js"
"http://localhost:63685/assets/studio.posts.editor.js"
]
},
"studio.collections": {
"js": [
"http://localhost:64876/assets/studio.collections.js"
"http://localhost:63685/assets/studio.collections.js"
]
}
}
Expand Down
16 changes: 8 additions & 8 deletions public/assets/manifest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"assets/app.css": "http://localhost:64876/assets/app.css",
"assets/app.js": "http://localhost:64876/assets/app.js",
"assets/post.js": "http://localhost:64876/assets/post.js",
"assets/stream.js": "http://localhost:64876/assets/stream.js",
"assets/file_manager.js": "http://localhost:64876/assets/file_manager.js",
"assets/tiptap_basic.js": "http://localhost:64876/assets/tiptap_basic.js",
"assets/studio.posts.editor.js": "http://localhost:64876/assets/studio.posts.editor.js",
"assets/studio.collections.js": "http://localhost:64876/assets/studio.collections.js"
"assets/app.css": "http://localhost:63685/assets/app.css",
"assets/app.js": "http://localhost:63685/assets/app.js",
"assets/post.js": "http://localhost:63685/assets/post.js",
"assets/stream.js": "http://localhost:63685/assets/stream.js",
"assets/file_manager.js": "http://localhost:63685/assets/file_manager.js",
"assets/tiptap_basic.js": "http://localhost:63685/assets/tiptap_basic.js",
"assets/studio.posts.editor.js": "http://localhost:63685/assets/studio.posts.editor.js",
"assets/studio.collections.js": "http://localhost:63685/assets/studio.collections.js"
}
65 changes: 65 additions & 0 deletions resources/views/studio/users/show.edge
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,69 @@
@end
@end

<div class="flex items-start justify-between mt-6">
<table>
<thead>
<tr>
<th class="p-2">Date</th>
<th class="p-2">Number</th>
<th class="p-2">Amount</th>
<th class="p-2">Status</th>
</tr>
</thead>
<tbody>
@each (invoice in invoices)
<tr>
<td class="p-2">{{ invoice.periodStartAt.toFormat('MMM dd, yyyy') }}</td>
<td class="p-2">{{ invoice.invoiceNumber }}</td>
<td class="p-2">{{ invoice.amountDue }}</td>
<td class="p-2">{{ invoice.status }}</td>
</tr>
@endeach
</tbody>
</table>

<form method="POST" action="{{ route('studio.users.invoices.store', { id: user.id }) }}">
{{ csrfField() }}
<label>
Invoice Id
@!form.input({ type: 'text', name: 'invoiceId' })
</label>
<label>
Invoice Number
@!form.input({ type: 'text', name: 'invoiceNumber' })
</label>
<label>
Charge Id
@!form.input({ type: 'text', name: 'chargeId' })
</label>
<label>
Amount Due
@!form.input({ type: 'number', name: 'amountDue' })
</label>
<label>
Amount Paid
@!form.input({ type: 'number', name: 'amountPaid' })
</label>
<label>
Amount Remaining
@!form.input({ type: 'number', name: 'amountRemaining' })
</label>
<label>
Status
@!form.input({ type: 'text', name: 'status' })
</label>
<label>
Period Start At
@!form.input({ type: 'number', name: 'periodStartAt', placeholder: 'Seconds timestamp' })
</label>
<label>
Period End At
@!form.input({ type: 'number', name: 'periodEndAt', placeholder: 'Seconds timestamp' })
</label>

<button type="submit">Create Invoice</button>
</form>
</div>

@endsection
1 change: 1 addition & 0 deletions start/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Route.group(() => {

Route.get('/', 'UsersController.index').as('index')
Route.get('/:id', 'UsersController.show').as('show').where('id', Route.matchers.number())
Route.post('/:id/invoice', 'InvoicesController.store').as('invoices.store').where('id', Route.matchers.number())
Route.patch('/:id/role', 'UsersController.role').as('role').where('id', Route.matchers.number())

Route.group(() => {
Expand Down

0 comments on commit cebff47

Please sign in to comment.