Introduces properties and loans as first-class financial entities: - models_property.go: Property, Loan, LoanView, PropertyView, PropertyData - store_property.go: full CRUD for finance_properties + finance_loans collections - handler_property.go: GET/POST /property with add/edit/delete for both entities; amortization helpers (EMI, remaining months, total interest) - templates/property.html: summary equity cards, property cards with equity bar and linked loan details, standalone loan cards with payoff progress - base.html: "Property" nav link added to desktop and mobile drawer - storeIface + mockStore updated with 10 new property/loan methods Co-authored-by: Gonçalo Rodrigues <guga@Goncalos-MacBook-Pro.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
104 lines
2.6 KiB
Go
104 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
"go.mongodb.org/mongo-driver/v2/mongo/options"
|
|
)
|
|
|
|
func (s *Store) getProperties(ctx context.Context, userID string) ([]Property, error) {
|
|
cur, err := s.db.Collection("finance_properties").Find(ctx,
|
|
bson.M{"user_id": userID},
|
|
options.Find().SetSort(bson.D{{Key: "created_at", Value: -1}}),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var out []Property
|
|
if err := cur.All(ctx, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *Store) getProperty(ctx context.Context, id, userID string) (*Property, error) {
|
|
var p Property
|
|
err := s.db.Collection("finance_properties").FindOne(ctx,
|
|
bson.M{"_id": id, "user_id": userID},
|
|
).Decode(&p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &p, nil
|
|
}
|
|
|
|
func (s *Store) createProperty(ctx context.Context, p *Property) error {
|
|
p.CreatedAt = time.Now()
|
|
_, err := s.db.Collection("finance_properties").InsertOne(ctx, p)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) updateProperty(ctx context.Context, id, userID string, update bson.M) error {
|
|
_, err := s.db.Collection("finance_properties").UpdateOne(ctx,
|
|
bson.M{"_id": id, "user_id": userID},
|
|
bson.M{"$set": update},
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) deleteProperty(ctx context.Context, id, userID string) error {
|
|
_, err := s.db.Collection("finance_properties").DeleteOne(ctx,
|
|
bson.M{"_id": id, "user_id": userID},
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) getLoans(ctx context.Context, userID string) ([]Loan, error) {
|
|
cur, err := s.db.Collection("finance_loans").Find(ctx,
|
|
bson.M{"user_id": userID},
|
|
options.Find().SetSort(bson.D{{Key: "created_at", Value: -1}}),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var out []Loan
|
|
if err := cur.All(ctx, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *Store) getLoan(ctx context.Context, id, userID string) (*Loan, error) {
|
|
var l Loan
|
|
err := s.db.Collection("finance_loans").FindOne(ctx,
|
|
bson.M{"_id": id, "user_id": userID},
|
|
).Decode(&l)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &l, nil
|
|
}
|
|
|
|
func (s *Store) createLoan(ctx context.Context, l *Loan) error {
|
|
l.CreatedAt = time.Now()
|
|
_, err := s.db.Collection("finance_loans").InsertOne(ctx, l)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) updateLoan(ctx context.Context, id, userID string, update bson.M) error {
|
|
_, err := s.db.Collection("finance_loans").UpdateOne(ctx,
|
|
bson.M{"_id": id, "user_id": userID},
|
|
bson.M{"$set": update},
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) deleteLoan(ctx context.Context, id, userID string) error {
|
|
_, err := s.db.Collection("finance_loans").DeleteOne(ctx,
|
|
bson.M{"_id": id, "user_id": userID},
|
|
)
|
|
return err
|
|
}
|