Skip to content

Commit

Permalink
WIP: credit card
Browse files Browse the repository at this point in the history
  • Loading branch information
ananthakumaran committed Jan 21, 2024
1 parent 10eb989 commit 8f0dbeb
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 11 deletions.
12 changes: 12 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ type AllocationTarget struct {
Accounts []string `json:"accounts" yaml:"accounts"`
}

type CreditCard struct {
Account string `json:"account" yaml:"account"`
CreditLimit uint64 `json:"credit_limit" yaml:"credit_limit"`
StatementEndDay uint64 `json:"statement_end_day" yaml:"statement_end_day"`
DueDay uint64 `json:"due_day" yaml:"due_day"`
Network string `json:"network" yaml:"network"`
Number string `json:"number" yaml:"number"`
}

type Config struct {
JournalPath string `json:"journal_path" yaml:"journal_path"`
DBPath string `json:"db_path" yaml:"db_path"`
Expand Down Expand Up @@ -142,6 +151,8 @@ type Config struct {
Goals Goals `json:"goals" yaml:"goals"`

UserAccounts []UserAccount `json:"user_accounts" yaml:"user_accounts"`

CreditCards []CreditCard `json:"credit_cards" yaml:"credit_cards"`
}

var config Config
Expand All @@ -164,6 +175,7 @@ var defaultConfig = Config{
Accounts: []Account{},
Goals: Goals{Retirement: []RetirementGoal{}, Savings: []SavingsGoal{}},
UserAccounts: []UserAccount{},
CreditCards: []CreditCard{},
}

var itemsUniquePropertiesMeta = jsonschema.MustCompileString("itemsUniqueProperties.json", `{
Expand Down
66 changes: 63 additions & 3 deletions internal/config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@
"properties": {
"name": {
"type": "string",
"description": "name of the commodity"
"description": "Name of the commodity"
},
"type": {
"type": "string",
Expand Down Expand Up @@ -395,7 +395,7 @@
"properties": {
"name": {
"type": "string",
"description": "name of the template",
"description": "Name of the template",
"minLength": 1
},
"content": {
Expand All @@ -422,7 +422,7 @@
"properties": {
"name": {
"type": "string",
"description": "name of the account",
"description": "Name of the account",
"minLength": 1
},
"icon": {
Expand All @@ -434,6 +434,66 @@
"required": ["name"],
"additionalProperties": false
}
},
"credit_cards": {
"type": "array",
"itemsUniqueProperties": ["account"],
"default": [
{
"account": "Liabilities:CreditCard:Chase",
"credit_limit": 100000,
"statement_end_day": 28,
"due_day": 15
}
],
"items": {
"type": "object",
"ui:header": "account",
"properties": {
"account": {
"type": "string",
"description": "Name of the credit card account"
},
"credit_limit": {
"type": "number",
"description": "Credit limit of the card",
"minimum": 1
},
"statement_end_day": {
"type": "integer",
"description": "Statement end day of the card",
"minimum": 1,
"maximum": 31
},
"due_day": {
"type": "integer",
"description": "Due day of the card",
"minimum": 1,
"maximum": 31
},
"network": {
"type": "string",
"description": "Network of the card",
"enum": ["visa", "mastercard", "dinersclub", "amex", "rupay", "jcb", "discover"]
},
"number": {
"type": "string",
"description": "Last 4 digits of the card number",
"maxLength": 4,
"minLength": 4,
"pattern": "^[0-9]{4}$"
}
},
"required": [
"account",
"credit_limit",
"statement_end_day",
"due_day",
"network",
"number"
],
"additionalProperties": false
}
}
},
"required": ["journal_path", "db_path"],
Expand Down
34 changes: 34 additions & 0 deletions internal/server/credit_card.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package server

import (
"github.com/ananthakumaran/paisa/internal/config"
"github.com/ananthakumaran/paisa/internal/model/posting"
"github.com/ananthakumaran/paisa/internal/query"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)

type CreditCardSummary struct {
Account string `json:"account"`
Network string `json:"network"`
Number string `json:"number"`
}

func GetCreditCards(db *gorm.DB) gin.H {
var creditCards []CreditCardSummary

for _, creditCardConfig := range config.GetConfig().CreditCards {
ps := query.Init(db).Where("account = ?", creditCardConfig.Account).All()
creditCards = append(creditCards, buildCreditCard(creditCardConfig, ps))
}

return gin.H{"creditCards": creditCards}
}

func buildCreditCard(creditCardConfig config.CreditCard, ps []posting.Posting) CreditCardSummary {
return CreditCardSummary{
Account: creditCardConfig.Account,
Network: creditCardConfig.Network,
Number: creditCardConfig.Number,
}
}
4 changes: 4 additions & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,10 @@ func Build(db *gorm.DB, enableCompression bool) *gin.Engine {
c.JSON(200, goal.GetGoalDetails(db, c.Param("type"), c.Param("name")))
})

router.GET("/api/credit_cards", func(c *gin.Context) {
c.JSON(200, GetCreditCards(db))
})

router.NoRoute(func(c *gin.Context) {
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(web.Index))
})
Expand Down
24 changes: 24 additions & 0 deletions src/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1088,3 +1088,27 @@ textarea:invalid {
div.is-hoverable:hover {
background-color: $white-bis;
}

// credit card

.credit-card-container {
display: grid;
gap: 18px;
grid-template-columns: repeat(auto-fill, minmax(19rem, 25rem));
}

.credit-card {
aspect-ratio: 3.375/2.125;
border-radius: 0.7rem;
display: flex;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
}

.has-debossed-text {
background-color: $grey-lighter;
-webkit-background-clip: text;
-moz-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: rgba($white, 0.5) 0px 3px 3px;
}
119 changes: 119 additions & 0 deletions src/lib/components/CreditCardNetwork.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<script lang="ts">
export let size: number;
export let name: string;
$: multiplier =
{
rupay: 1.3,
discover: 1.7
}[name] || 1;
</script>

{#if name == "visa"}
<div style="margin-bottom: -{size / 2.9}px">
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 24 24"
><path
fill="currentColor"
d="M9.112 8.262L5.97 15.758H3.92L2.374 9.775c-.094-.368-.175-.503-.461-.658C1.447 8.864.677 8.627 0 8.479l.046-.217h3.3a.904.904 0 0 1 .894.764l.817 4.338l2.018-5.102zm8.033 5.049c.008-1.979-2.736-2.088-2.717-2.972c.006-.269.262-.555.822-.628a3.66 3.66 0 0 1 1.913.336l.34-1.59a5.207 5.207 0 0 0-1.814-.333c-1.917 0-3.266 1.02-3.278 2.479c-.012 1.079.963 1.68 1.698 2.04c.756.367 1.01.603 1.006.931c-.005.504-.602.725-1.16.734c-.975.015-1.54-.263-1.992-.473l-.351 1.642c.453.208 1.289.39 2.156.398c2.037 0 3.37-1.006 3.377-2.564m5.061 2.447H24l-1.565-7.496h-1.656a.883.883 0 0 0-.826.55l-2.909 6.946h2.036l.405-1.12h2.488zm-2.163-2.656l1.02-2.815l.588 2.815zm-8.16-4.84l-1.603 7.496H8.34l1.605-7.496z"
/></svg
>
</div>
{/if}

{#if name == "mastercard"}
<div style="margin-bottom: -{size / 3}px">
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 24 24"
><path
fill="currentColor"
d="M15.273 18.728A6.728 6.728 0 1 1 22 11.999V12a6.735 6.735 0 0 1-6.727 6.728"
opacity=".5"
/><path
fill="currentColor"
d="M8.727 18.728A6.728 6.728 0 1 1 15.455 12a6.735 6.735 0 0 1-6.728 6.728"
/></svg
>
</div>
{/if}

{#if name == "dinersclub"}
<div style="margin-bottom: -{size / 6}px">
<svg
xmlns="http://www.w3.org/2000/svg"
width={size * 0.65}
height={size * 0.65}
viewBox="0 0 24 24"
><path
fill="currentColor"
d="M16.506 11.982a6.026 6.026 0 0 0-3.866-5.618V17.6a6.025 6.025 0 0 0 3.866-5.618M8.33 17.598V6.365a6.03 6.03 0 0 0-3.863 5.617a6.028 6.028 0 0 0 3.863 5.616m2.156-15.113A9.497 9.497 0 0 0 .99 11.982a9.495 9.495 0 0 0 9.495 9.494a9.495 9.495 0 0 0 9.496-9.494a9.499 9.499 0 0 0-9.496-9.497Zm-.023 19.888C4.723 22.4 0 17.75 0 12.09C0 5.905 4.723 1.626 10.463 1.627h2.69C18.822 1.627 24 5.903 24 12.09c0 5.658-5.176 10.283-10.848 10.283"
/></svg
>
</div>
{/if}

{#if name == "amex"}
<div style="margin-bottom: -{size / 2}px">
<svg
xmlns="http://www.w3.org/2000/svg"
width={size * 1.2}
height={size * 1.2}
viewBox="0 0 24 24"
><path
fill="currentColor"
fill-rule="evenodd"
d="m4.314 11.965l-.82-1.997l-.815 1.997zm7.859 2.161l-.005-3.922l-1.736 3.922h-1.05L7.64 10.2v3.926H5.206l-.46-1.117H2.253l-.465 1.117h-1.3l2.144-5.008H4.41l2.036 4.742V9.118H8.4l1.567 3.397l1.439-3.397H13.4v5.008zm3.133-1.024v-.997h2.628v-1.022h-2.628v-.911h3.001l1.31 1.46l-1.368 1.47zm8.111 1.044h-1.556l-1.474-1.659l-1.532 1.659h-4.742v-5.01h4.815l1.473 1.642l1.523-1.642h1.564l-2.327 2.505z"
/></svg
>
</div>
{/if}

{#if name == "rupay"}
<div style="margin: -{size / 1.9}px -{size * 1.7}px">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 333334 199007"
width={size * 3.8 * multiplier}
height={size * multiplier}
shape-rendering="geometricPrecision"
text-rendering="geometricPrecision"
image-rendering="optimizeQuality"
fill-rule="evenodd"
clip-rule="evenodd"
><path
d="M214088 83928h13199v20970l11418-20970h12113l-24422 42395s-2267 3556-5079 5437c-2310 1547-5151 1477-6019 1540-4824-42-10643-55-10643-55l2807-10106 4542-8s2079-212 2882-1237c765-977 1156-1954 1156-3387 0-2148-1954-34580-1954-34580zM76939 88116c-1837 4256-7533 3772-7533 3772l-6632-31 2421-9013s5933 22 8843 22c3115 0 4088 2502 2902 5249zm15073-6142c1129-8943-6741-11201-15250-11201H54402l-13199 48105h14208l4436-16333 7970 65s3289-191 3354 2898c69 3295-2442 9345-2280 13370h14596l-32-1281s-1213-322-1078-2026c56-709 839-2953 1864-5979 618-1334 1550-4499 1464-7076-107-3217-2126-4710-5037-5754 9074-2128 11342-14787 11343-14787zm3224 1954h12959l-5337 20533s-1331 4579 2952 4932c3384 280 5902-3758 6727-6493 1084-3592 5296-18973 5296-18973h13351l-10159 34950h-11657l1432-4993s-5947 7252-14783 6382c-7854-771-8531-6468-7170-13575 668-3489 6389-22763 6389-22763zm66557 4298c-1849 5158-6944 4550-6944 4550l-6956 2 2746-10220s4403 23 7311 23c3560 0 4852 2828 3843 5644zm14609-4776c1130-8944-5687-12678-14197-12678h-22359l-13198 48105h14208l3995-14831 11320 69s17530 742 20232-20665zm11945 28307c-2220 563-4912 869-5446-1148-1466-5527 11474-7145 11474-7145 88 5036-4325 7859-6029 8293zm19575-10116c1707-5814 3865-11319 2128-14370-2660-4670-7468-5080-14502-5080-7771 0-17365 1476-20492 11810h12937s1179-3892 6035-3647c4298 217 4063 3174 2479 4809-2778 2865-10450 1276-18947 4224-7424 2576-10022 12338-8409 16234 1563 3778 4474 4259 8401 4645 6307 620 11143-2897 13394-4961 0 2292 60 3572 60 3572h13614l-33-1281s-1213-322-1078-2026c98-1248 2450-7252 4412-13929z"
fill="currentColor"
/><path fill="currentColor" d="M267751 75852l-15239 53011 28524-26506z" /><path
fill="currentColor"
d="M257982 75852l-15239 53011 28525-26506z"
/></svg
>
</div>
{/if}

{#if name == "jcb"}
<div style="margin: -{size / 2.2}px 0">
<svg
xmlns="http://www.w3.org/2000/svg"
width={size * multiplier}
height={size * multiplier}
viewBox="0 0 24 24"
><path
fill="currentColor"
d="M13.05 9.864c.972.074 1.726.367 2.355.685v-1.31s-1.258-.317-2.441-.368C8.838 8.686 7.669 10.305 7.669 12s1.17 3.314 5.295 3.13c1.183-.054 2.44-.37 2.44-.37v-1.309c-.619.308-1.382.611-2.354.683c-1.68.128-2.69-.69-2.69-2.134c0-1.445 1.01-2.261 2.69-2.135m7.685 4.122a1.48 1.48 0 0 1-.215.02h-1.8v-1.631h1.8c.057 0 .164.01.215.02a.806.806 0 0 1 .632.795a.804.804 0 0 1-.632.796M18.72 9.95h1.632c.059 0 .145.007.177.013a.736.736 0 0 1 .626.74a.735.735 0 0 1-.626.739a1.571 1.571 0 0 1-.178.013h-1.63zm3.499 1.985V11.9c.913-.133 1.415-.726 1.415-1.42c0-.883-.734-1.392-1.73-1.442c-.077-.003-.202-.01-.304-.01h-5.332v5.946h5.755c1.13 0 1.977-.604 1.977-1.547c0-.87-.772-1.422-1.781-1.491zm-17.864.68c0 .878-.591 1.53-1.666 1.53c-.917 0-1.817-.272-2.689-.694v1.309s1.402.383 3.191.383c2.971 0 3.837-1.125 3.837-2.529V9.027H4.354z"
/></svg
>
</div>
{/if}

{#if name == "discover"}
<div style="margin-bottom: -{size / 1.35}px">
<svg
xmlns="http://www.w3.org/2000/svg"
width={size * multiplier}
height={size * multiplier}
viewBox="0 0 24 24"
><path
fill="currentColor"
d="M14.58 12a2.023 2.023 0 1 1-2.025-2.023h.002c1.118 0 2.023.906 2.023 2.023m-5.2-2.001c-1.124 0-2.025.884-2.025 1.99c0 1.118.878 1.984 2.007 1.984c.319 0 .593-.063.93-.221v-.873c-.296.297-.559.416-.895.416c-.747 0-1.277-.542-1.277-1.312c0-.73.547-1.306 1.243-1.306c.354 0 .622.126.93.428v-.873a1.898 1.898 0 0 0-.913-.233m-3.352 1.545c-.445-.165-.576-.273-.576-.479c0-.239.233-.422.553-.422c.222 0 .405.091.598.308l.388-.508a1.665 1.665 0 0 0-1.117-.422c-.673 0-1.186.467-1.186 1.089c0 .524.239.792.936 1.043c.291.103.438.171.513.217a.456.456 0 0 1 .222.394c0 .308-.245.536-.576.536c-.354 0-.639-.177-.809-.507l-.479.461c.342.502.752.724 1.317.724c.771 0 1.311-.513 1.311-1.249c-.002-.603-.252-.876-1.095-1.185M24 10.3a.29.29 0 0 1-.288.291a.29.29 0 0 1-.291-.291v-.003A.29.29 0 1 1 24 10.3m-.059.001a.235.235 0 0 0-.231-.239a.234.234 0 0 0-.232.239c0 .132.104.239.232.239a.235.235 0 0 0 .231-.239M3.472 13.887h.742v-3.803h-.742zm12.702-1.248l-1.014-2.554h-.81l1.614 3.9h.399l1.643-3.9h-.804zm2.166 1.248h2.104v-.644h-1.362v-1.027h1.312v-.644h-1.312v-.844h1.362v-.644H18.34zm5.409-3.557l.11.138h-.097l-.094-.13v.13h-.08v-.334h.107c.081 0 .126.036.126.103c.001.046-.025.08-.072.093m-.006-.092c0-.029-.021-.043-.06-.043h-.014v.087h.014c.039 0 .06-.014.06-.044m-1.228 2.047l1.197 1.602H22.8l-1.027-1.528h-.097v1.528h-.741v-3.803h1.1c.855 0 1.346.411 1.346 1.123c0 .583-.308.965-.866 1.078m.103-1.038c0-.37-.251-.563-.713-.563h-.228v1.152h.217c.473-.001.724-.207.724-.589m-19.487.742a1.91 1.91 0 0 1-.69 1.46c-.365.303-.781.439-1.357.439H.001v-3.803H1.09c1.202 0 2.041.781 2.041 1.904m-.764-.006c0-.364-.154-.718-.411-.947c-.245-.222-.536-.308-1.015-.308H.742v2.515h.199c.479 0 .782-.092 1.015-.302c.256-.228.411-.593.411-.958"
/></svg
>
</div>
{/if}
1 change: 1 addition & 0 deletions src/lib/components/Navbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
href: "/liabilities",
children: [
{ label: "Balance", href: "/balance" },
{ label: "Credit Card", href: "/credit_card", help: "credit-card" },
{ label: "Repayment", href: "/repayment" },
{ label: "Interest", href: "/interest" }
]
Expand Down
8 changes: 8 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,12 @@ export interface Log {
msg: string;
}

export interface CreditCardSummary {
account: string;
network: string;
number: string;
}

export interface GoalSummary {
type: string;
name: string;
Expand Down Expand Up @@ -609,6 +615,8 @@ export function ajax(route: "/api/liabilities/interest"): Promise<{
interest_timeline_breakdown: Interest[];
}>;

export function ajax(route: "/api/credit_cards"): Promise<{ creditCards: CreditCardSummary[] }>;

export function ajax(route: "/api/goals"): Promise<{ goals: GoalSummary[] }>;
export function ajax(
route: "/api/goals/retirement/:name",
Expand Down
Loading

0 comments on commit 8f0dbeb

Please sign in to comment.