Skip to content

Commit d6a2960

Browse files
authored
Merge pull request #664 from MicroPyramid/sf_import
feat: Enhance `seed_data` command with currency, country, and Faker l…
2 parents 2ca6afa + a6a4b67 commit d6a2960

6 files changed

Lines changed: 377 additions & 604 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ A modern, open-source CRM platform built with Django REST Framework and SvelteKi
99
![Svelte](https://img.shields.io/badge/svelte-5-orange.svg)
1010
![Coverage](./coverage-badge.svg)
1111

12+
<https://github.com/MicroPyramid/Django-CRM/raw/master/docs/media/demo.webm>
13+
1214
## Overview
1315

1416
BottleCRM is a full-featured Customer Relationship Management system designed for startups and small businesses. It combines a powerful Django REST API backend with a modern SvelteKit frontend, featuring multi-tenant architecture with PostgreSQL Row-Level Security (RLS) for enterprise-grade data isolation.

backend/common/management/commands/seed_data.py

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
Usage:
55
python manage.py seed_data --email admin@example.com
66
python manage.py seed_data --email admin@example.com --orgs 2 --leads 100 --seed 42
7+
python manage.py seed_data --email admin@example.com --currency EUR --country DE
78
python manage.py seed_data --email admin@example.com --clear --no-input
89
"""
910

@@ -82,6 +83,24 @@ class Command(BaseCommand):
8283
"Inbound",
8384
]
8485

86+
# Country code to Faker locale mapping
87+
COUNTRY_FAKER_LOCALES = {
88+
"US": "en_US",
89+
"GB": "en_GB",
90+
"CA": "en_CA",
91+
"AU": "en_AU",
92+
"DE": "de_DE",
93+
"FR": "fr_FR",
94+
"IN": "en_IN",
95+
"JP": "ja_JP",
96+
"CN": "zh_CN",
97+
"BR": "pt_BR",
98+
"MX": "es_MX",
99+
"SG": "en_SG",
100+
"AE": "ar_AE",
101+
"CH": "de_CH",
102+
}
103+
85104
# Team name templates
86105
TEAM_NAMES = [
87106
("Sales Team", "Primary sales team handling all inbound leads"),
@@ -182,6 +201,22 @@ def add_arguments(self, parser):
182201
help="Tags per organization (default: 5)",
183202
)
184203

204+
# Locale options
205+
valid_currencies = [c[0] for c in CURRENCY_CODES]
206+
parser.add_argument(
207+
"--currency",
208+
type=str,
209+
default="USD",
210+
choices=valid_currencies,
211+
help=f"Default currency for organizations (default: USD). Choices: {', '.join(valid_currencies)}",
212+
)
213+
parser.add_argument(
214+
"--country",
215+
type=str,
216+
default="US",
217+
help="Default country code for organizations (default: US). Examples: US, GB, CA, AU, DE, FR, IN",
218+
)
219+
185220
# Invoice-related arguments
186221
parser.add_argument(
187222
"--products",
@@ -238,19 +273,25 @@ def add_arguments(self, parser):
238273
)
239274

240275
def handle(self, *args, **options):
241-
# Initialize Faker
276+
# Initialize Faker with locale matching the country
277+
country = options["country"].upper()
278+
locale = self.COUNTRY_FAKER_LOCALES.get(country, "en_US")
279+
242280
seed = options.get("seed")
243281
if seed:
244282
random.seed(seed)
245-
self.fake = Faker(["en_US"])
283+
self.fake = Faker([locale])
246284
Faker.seed(seed)
247285
else:
248-
self.fake = Faker(["en_US", "en_GB", "en_CA", "en_AU"])
286+
self.fake = Faker([locale])
249287

250288
# Initialize InvoiceSeeder
251289
self.invoice_seeder = InvoiceSeeder(self.fake, self.stdout)
252290

253291
self.stdout.write(self.style.MIGRATE_HEADING("Seeding CRM database..."))
292+
self.stdout.write(
293+
f"Currency: {options['currency']}, Country: {country}, Locale: {locale}"
294+
)
254295
if seed:
255296
self.stdout.write(f"Using seed: {seed}")
256297

@@ -334,7 +375,7 @@ def seed_all(self, options):
334375
"""Main seeding orchestration."""
335376
for i in range(options["orgs"]):
336377
self.stdout.write(f"\n--- Organization {i + 1}/{options['orgs']} ---")
337-
org = self.create_org()
378+
org = self.create_org(options["currency"], options["country"])
338379
# Set RLS context for this org before creating org-scoped data
339380
self.set_rls_context(org.id)
340381
profiles = self.create_profiles(
@@ -404,15 +445,12 @@ def seed_all(self, options):
404445
options["tasks"],
405446
)
406447

407-
def create_org(self):
448+
def create_org(self, currency, country):
408449
"""Create an organization."""
409-
currencies = [c[0] for c in CURRENCY_CODES]
410-
countries = ["US", "GB", "CA", "AU", "DE", "FR", "IN"]
411-
412450
org = Org.objects.create(
413451
name=self.fake.company(),
414-
default_currency=random.choice(currencies),
415-
default_country=random.choice(countries),
452+
default_currency=currency,
453+
default_country=country,
416454
)
417455
self.stats["orgs"] += 1
418456
self.stdout.write(
@@ -524,7 +562,7 @@ def create_contacts(self, org, profiles, teams, tags, count):
524562
city=self.fake.city(),
525563
state=self.fake.state_abbr(),
526564
postcode=self.fake.postcode(),
527-
country=random.choice(["US", "GB", "CA", "AU"]),
565+
country=org.default_country or "US",
528566
description=self.fake.paragraph() if random.random() > 0.5 else None,
529567
org=org,
530568
)

frontend/package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,23 @@
1818
"@eslint/js": "^10.0.1",
1919
"@internationalized/date": "^3.11.0",
2020
"@lucide/svelte": "^0.575.0",
21-
"@sveltejs/adapter-node": "^5.5.3",
22-
"@sveltejs/kit": "^2.53.0",
21+
"@sveltejs/adapter-node": "^5.5.4",
22+
"@sveltejs/kit": "^2.53.4",
2323
"@sveltejs/vite-plugin-svelte": "^7.0.0",
2424
"@tailwindcss/typography": "^0.5.19",
2525
"@tailwindcss/vite": "^4.2.1",
26-
"@types/node": "^25.3.0",
26+
"@types/node": "^25.3.3",
2727
"bits-ui": "^2.16.2",
2828
"eslint": "^10.0.2",
2929
"eslint-config-prettier": "^10.1.8",
3030
"eslint-plugin-svelte": "^3.15.0",
31-
"globals": "^17.3.0",
31+
"globals": "^17.4.0",
3232
"mode-watcher": "^1.1.0",
3333
"prettier": "^3.8.1",
3434
"prettier-plugin-svelte": "^3.5.0",
3535
"prettier-plugin-tailwindcss": "^0.7.2",
36-
"svelte": "^5.53.3",
37-
"svelte-check": "^4.4.3",
36+
"svelte": "^5.53.6",
37+
"svelte-check": "^4.4.4",
3838
"svelte-sonner": "^1.0.7",
3939
"tailwindcss": "^4.2.1",
4040
"typescript": "^5.9.3",
@@ -49,11 +49,11 @@
4949
}
5050
},
5151
"dependencies": {
52-
"@sentry/sveltekit": "^10.39.0",
53-
"axios": "^1.13.5",
52+
"@sentry/sveltekit": "^10.40.0",
53+
"axios": "^1.13.6",
5454
"clsx": "^2.1.1",
5555
"date-fns": "^4.1.0",
56-
"libphonenumber-js": "^1.12.37",
56+
"libphonenumber-js": "^1.12.38",
5757
"svelte-dnd-action": "^0.9.69",
5858
"tailwind-merge": "^3.5.0",
5959
"tailwind-variants": "^3.2.2",

0 commit comments

Comments
 (0)