Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions script/silver/dbt_project/dbt_project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,39 @@ version: '1.0.0'
profile: 'dbt_project'

# These configurations specify where dbt should look for different types of files.
# The `model-paths` config, for example, states that models in this project can be
# found in the "models/" directory. You probably won't need to change these!
model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]

clean-targets: # directories to be removed by `dbt clean`
clean-targets:
- "target"
- "dbt_packages"


# Configuring models
# Full documentation: https://docs.getdbt.com/docs/configuring-models

# In this example config, we tell dbt to build all models in the example/
# directory as views. These settings can be overridden in the individual model
# files using the `{{ config(...) }}` macro.
# Configuring models with Medallion Architecture layers
models:
dbt_project:
silver:
+materialized: table

# ─── Staging Layer (Bronze → Silver Raw Cleaning) ───
staging:
+materialized: view
+schema: silver

# ─── Intermediate Layer (Business Transformations) ───
intermediate:
+materialized: view
+schema: silver

# ─── Marts Layer (Gold Layer - Business Ready) ───
marts:
dimensions:
+materialized: table
+schema: gold
Comment on lines +39 to +41

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Align marts config path names with model directories

These configuration keys use lowercase path segments (dimensions/facts/reporting), but the actual model folders are models/marts/Dimensions, models/marts/Facts, and models/marts/Reporting; dbt treats unmatched paths as unused config, so the intended materialization/schema settings for marts will not be applied to those models.

Useful? React with 👍 / 👎.

facts:
+materialized: table
+schema: gold
reporting:
+materialized: view
Comment on lines +39 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== marts directory names =="
fd -t d . script/silver/dbt_project/models/marts

echo
echo "== marts config keys in dbt_project.yml =="
rg -n '^\s{6}(dimensions|facts|reporting|Dimensions|Facts|Reporting):' script/silver/dbt_project/dbt_project.yml

Repository: Ritik574-coder/dbt_learning_project

Length of output: 355


🌐 Web query:

dbt project.yml models path config keys match directory names case sensitive

💡 Result:

In dbt, the dbt_project.yml keys under models: are resource paths (project-name namespaced), and the path segments must match the actual model filename/subdirectory structure including case. 1) Where model path config keys live in dbt_project.yml - You configure models in dbt_project.yml under models: using a namespaced dictionary where the top-level key is your project name, and nested keys represent subdirectories under your models path. This is described in dbt’s dbt_project.yml and model configuration docs. [1][2] 2) Case sensitivity: model filenames/subdirectories and config matching - dbt requires the model name to match the filename (including case sensitivity). If the casing doesn’t match, dbt may not apply configurations correctly. [2] - Additionally, when defining model properties in YAML via define properties, dbt notes that the name in the YAML must match the model filename “including case sensitivity.” This is another explicit statement about case-sensitive matching between dbt config/property entries and model files. [3] 3) Directory path matching rules (practical implication) - Because the nested models: keys correspond to the relative filesystem path segments under your configured model-paths directories, and because dbt’s model name/filename matching is case sensitive, treat your directory names and filenames as case sensitive when matching them to the keys in dbt_project.yml. [1][4][2][3] 4) Keys under models: are not arbitrary - The doc for model-configs emphasizes that resource paths must be expressed as nested dictionary keys (with the most specific/ deepest configuration taking precedence). [1] So for your query: yes—config keys that represent directory/file path segments in dbt_project.yml must match the actual model paths (including case), because dbt’s model name/filename matching is explicitly case sensitive and dbt’s models: configuration uses those path segments to target models. [2][3][1]

Citations:


Fix casing mismatch for marts model config keys (dbt is case-sensitive)

In script/silver/dbt_project/dbt_project.yml (lines 39-46), the marts: config keys are dimensions/facts/reporting (lowercase), but the actual model directories are models/marts/Dimensions, models/marts/Facts, and models/marts/Reporting (capitalized). dbt’s config/path matching is case-sensitive, so +materialized/+schema may not apply to the intended models.

Suggested fix
     marts:
-      dimensions:
+      Dimensions:
         +materialized: table
         +schema: gold
-      facts:
+      Facts:
         +materialized: table
         +schema: gold
-      reporting:
+      Reporting:
         +materialized: view
         +schema: gold
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dimensions:
+materialized: table
+schema: gold
facts:
+materialized: table
+schema: gold
reporting:
+materialized: view
Dimensions:
materialized: table
schema: gold
Facts:
materialized: table
schema: gold
Reporting:
materialized: view
schema: gold
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@script/silver/dbt_project/dbt_project.yml` around lines 39 - 46, The marts
model config uses lowercase keys (dimensions, facts, reporting) which don't
match the actual model directory names (Dimensions, Facts, Reporting) so dbt
won't apply +materialized/+schema; update the config keys under the marts block
to match exact case of the directories (use Dimensions, Facts, Reporting) so
that the existing +materialized and +schema settings are applied to those
models.

+schema: gold
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
-- Intermediate Model: int_customers_standardized
-- Purpose: Further refine customer data with business-specific transformations

WITH staging AS (
SELECT * FROM {{ ref('src_customers') }}
)

SELECT
customer_id,
title,
first_name,
last_name,
CONCAT(first_name, ' ', last_name) AS full_name,
gender,
date_of_birth,
age,

CASE
WHEN age BETWEEN 0 AND 17 THEN 'Under 18'
WHEN age BETWEEN 18 AND 24 THEN '18-24'
WHEN age BETWEEN 25 AND 34 THEN '25-34'
WHEN age BETWEEN 35 AND 44 THEN '35-44'
WHEN age BETWEEN 45 AND 54 THEN '45-54'
WHEN age BETWEEN 55 AND 64 THEN '55-64'
WHEN age >= 65 THEN '65+'
ELSE 'Unknown'
END AS age_group,

email,
phone,
address,
city,
state_abbr,
state_full,
zip_code,
country,
region,
customer_segment,
loyalty_points,
is_active,
account_created_date,
preferred_channel,
annual_income_usd,
company,
GETDATE() AS _loaded_at
FROM staging
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- Intermediate Model: int_employees_standardized

WITH staging AS (
SELECT * FROM {{ ref('src_employees') }}
)

SELECT
employee_id,
first_name,
last_name,
CONCAT(first_name, ' ', last_name) AS full_name,
email,
phone,
job_title,
department,
store_id,
store_name,
store_city,
hire_date,
years_employed,
annual_salary_usd,
commission_rate_pct,
is_active,
performance_rating,
manager_id,
DATEDIFF(YEAR, hire_date, GETDATE()) AS calculated_tenure_years,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="script/silver/dbt_project/models/intermediate/Employees/int_employees_standardized.sql"

echo "== File header =="
sed -n '1,120p' "$FILE" | nl -ba | sed -n '1,120p'

echo
echo "== Context around calculated_tenure_years (lines 1..80 from around line 26) =="
# Print a safe window around line 26 (line numbers shown via nl)
START=10
END=60
nl -ba "$FILE" | sed -n "${START},${END}p"

echo
echo "== Search for hire_date usage/casting in this model =="
rg -n "hire_date|DATEDIFF\\(YEAR|calculated_tenure_years|tenure" "$FILE" || true

echo
echo "== Search repo for other tenure-year calculations patterns =="
rg -n "DATEDIFF\\(YEAR,\\s*hire_date|calculated_tenure_years|tenure_years" script/silver/dbt_project/models || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="script/silver/dbt_project/models/intermediate/Employees/int_employees_standardized.sql"

echo "== Exists? =="
ls -la "$FILE"

echo
echo "== File contents around start (first 120 lines) with line numbers =="
awk '{printf "%5d\t%s\n", NR, $0}' "$FILE" | sed -n '1,120p'

echo
echo "== Context around calculated_tenure_years (around line 26) =="
awk '{printf "%5d\t%s\n", NR, $0}' "$FILE" | sed -n '10,60p'

echo
echo "== Search within file for hire_date / calculated_tenure_years / tenure =="
rg -n "hire_date|DATEDIFF\(YEAR|calculated_tenure_years|tenure" "$FILE" || true

echo
echo "== Search repo for other tenure-year calculations patterns =="
rg -n "DATEDIFF\(YEAR,\s*hire_date|calculated_tenure_years|tenure_years" script/silver/dbt_project/models || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 2360


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILES=(
  "script/silver/dbt_project/models/marts/Reporting/employee_performance.sql"
  "script/silver/dbt_project/models/marts/Dimensions/dim_employees.sql"
)

for f in "${FILES[@]}"; do
  echo "==================== $f ===================="
  ls -la "$f"
  echo "-- contents (with line numbers) --"
  awk '{printf "%5d\t%s\n", NR, $0}' "$f" | sed -n '1,120p'
  echo
done

echo "== Search for calculated_tenure_years definitions/overrides =="

rg -n "calculated_tenure_years\s*=|AS\s+calculated_tenure_years|DATEDIFF\\(YEAR,\\s*hire_date|tenure_years" script/silver/dbt_project/models || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 2984


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "years_employed|calculated_tenure_years" script/silver/dbt_project/models || true

echo "== Search for hire_date type/casts in src_employees model =="

# Try common model names; list files that might define src_employees
fd "src_employees" script/silver/dbt_project/models || true
fd "src_employees*" script/silver/dbt_project/models || true

# Search for the src_employees definition if present
rg -n "ref\\('src_employees'\\)" script/silver/dbt_project/models || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 1987


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="script/silver/dbt_project/models/staging/src_employees.sql"
echo "== Exists? =="
ls -la "$FILE"

echo
echo "== Find hire_date handling in src_employees.sql =="
rg -n "hire_date" "$FILE" || true

echo
echo "== Show section around hire_date casts/logic (lines 1..220, then narrowed to matches) =="
awk '{printf "%5d\t%s\n", NR, $0}' "$FILE" | sed -n '1,220p' | tail -n +1

echo
echo "== Show near hire_date occurrences =="
# Extract line ranges around each match (simple approach: print fixed window around first few matches)
MATCH_LINES=$(rg -n "hire_date" "$FILE" | cut -d: -f1 | head -n 10 | tr '\n' ' ')
echo "Match lines: $MATCH_LINES"
for l in $MATCH_LINES; do
  START=$((l-15)); END=$((l+25));
  awk '{printf "%5d\t%s\n", NR, $0}' "$FILE" | sed -n "${START},${END}p"
  echo "---"
done

Repository: Ritik574-coder/dbt_learning_project

Length of output: 31416


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find all usages of years_employed and calculated_tenure_years across models =="
rg -n "years_employed|calculated_tenure_years" script/silver/dbt_project/models || true

echo
echo "== Check for any tenure calculation helpers (e.g., DATEADD/DATEDIFF YEAR adjustments) =="
rg -n "DATEDIFF\\(YEAR,\\s*|DATEADD\\(YEAR,\\s*DATEDIFF\\(YEAR" script/silver/dbt_project/models || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 2152


Fix calculated_tenure_years off-by-one from DATEDIFF(YEAR, ...)

script/silver/dbt_project/models/intermediate/Employees/int_employees_standardized.sql line 26 uses DATEDIFF(YEAR, hire_date, GETDATE()), which counts year boundaries and can overstate completed tenure before the anniversary date.

💡 Proposed fix
-    DATEDIFF(YEAR, hire_date, GETDATE()) AS calculated_tenure_years,
+    DATEDIFF(YEAR, hire_date, CAST(GETDATE() AS DATE))
+      - CASE
+          WHEN DATEADD(YEAR, DATEDIFF(YEAR, hire_date, CAST(GETDATE() AS DATE)), hire_date) > CAST(GETDATE() AS DATE)
+          THEN 1 ELSE 0
+        END AS calculated_tenure_years,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
DATEDIFF(YEAR, hire_date, GETDATE()) AS calculated_tenure_years,
DATEDIFF(YEAR, hire_date, CAST(GETDATE() AS DATE))
- CASE
WHEN DATEADD(YEAR, DATEDIFF(YEAR, hire_date, CAST(GETDATE() AS DATE)), hire_date) > CAST(GETDATE() AS DATE)
THEN 1 ELSE 0
END AS calculated_tenure_years,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@script/silver/dbt_project/models/intermediate/Employees/int_employees_standardized.sql`
at line 26, The calculated_tenure_years uses DATEDIFF(YEAR, hire_date,
GETDATE()) which can be off-by-one; change it to compute completed years by
taking the DATEDIFF(YEAR, hire_date, GETDATE()) value and subtracting 1 when the
current date is before the hire anniversary in the current year. Concretely,
replace the direct DATEDIFF(YEAR, hire_date, GETDATE()) expression used for
calculated_tenure_years with a conditional that checks whether DATEADD/YEAR of
that DATEDIFF applied to hire_date is greater than GETDATE() (or alternatively
compare month/day of GETDATE() vs hire_date) and subtracts one in that case so
calculated_tenure_years reflects full completed years.

GETDATE() AS _loaded_at
FROM staging
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- Intermediate Model: int_inventory_summary

WITH staging AS (
SELECT * FROM {{ ref('src_inventory') }}
)

SELECT
snapshot_date,
product_id,
product_name,
sku,
category,
stock_on_hand,
stock_reserved,
stock_available,
reorder_level,
unit_cost,
unit_price,
inventory_value,
CASE WHEN stock_available <= reorder_level THEN 'Yes' ELSE 'No' END AS needs_reorder,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show the exact line context around the reported CASE expression
file="script/silver/dbt_project/models/intermediate/Inventory/int_inventory_summary.sql"
if [ -f "$file" ]; then
  echo "=== Context for $file (lines 1-80) ==="
  sed -n '1,120p' "$file" | nl -ba | sed -n '1,120p'
else
  echo "File not found: $file" >&2
  exit 1
fi

# 2) Find other occurrences of needs_reorder / reorder_level / stock_available logic
echo "=== rg: needs_reorder ==="
rg -n "needs_reorder" script/silver/dbt_project/models || true

echo "=== rg: stock_available"===
rg -n "stock_available" script/silver/dbt_project/models/intermediate/Inventory || true

echo "=== rg: reorder_level"===
rg -n "reorder_level" script/silver/dbt_project/models/intermediate/Inventory || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 255


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="script/silver/dbt_project/models/intermediate/Inventory/int_inventory_summary.sql"
if [ -f "$file" ]; then
  echo "=== Context for $file (lines 1-80) ==="
  sed -n '1,120p' "$file" | awk '{printf "%d\t%s\n", NR, $0}'
else
  echo "File not found: $file" >&2
  exit 1
fi

echo "=== rg: needs_reorder ==="
rg -n "needs_reorder" script/silver/dbt_project/models || true

echo "=== rg: stock_available ==="
rg -n "stock_available" script/silver/dbt_project/models || true

echo "=== rg: reorder_level ==="
rg -n "reorder_level" script/silver/dbt_project/models || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 4583


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show relevant parts of the intermediate model
file="script/silver/dbt_project/models/intermediate/Inventory/int_inventory_summary.sql"
echo "=== int_inventory_summary.sql (full) ==="
sed -n '1,200p' "$file" | awk '{printf "%d\t%s\n", NR, $0}'

# Show how stock_available + reorder_level are produced in staging
file2="script/silver/dbt_project/models/staging/src_inventory.sql"
echo "=== src_inventory.sql (stock_available + reorder_level area) ==="
# Print around the relevant region
sed -n '50,120p' "$file2" | awk '{printf "%d\t%s\n", NR, $0}'

# Show downstream usage of needs_reorder
file3="script/silver/dbt_project/models/marts/Reporting/inventory_summary.sql"
echo "=== inventory_summary.sql (uses needs_reorder) ==="
sed -n '1,120p' "$file3" | awk '{printf "%d\t%s\n", NR, $0}'

file4="script/silver/dbt_project/models/marts/Facts/fct_inventory.sql"
echo "=== fct_inventory.sql (selects needs_reorder) ==="
sed -n '1,120p' "$file4" | awk '{printf "%d\t%s\n", NR, $0}'

# Look for schema/tests/docs mentioning needs_reorder
echo "=== rg: needs_reorder in schema.yml ==="
rg -n "needs_reorder" script/silver/dbt_project/models/marts/schema.yml || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 5558


Preserve unknown reorder status when stock_available or reorder_level is NULL.

CASE WHEN stock_available <= reorder_level ... ELSE 'No' labels rows as 'No' when either input is NULL (comparison yields UNKNOWN), producing a false negative for missing data.

💡 Proposed fix
-    CASE WHEN stock_available <= reorder_level THEN 'Yes' ELSE 'No' END AS needs_reorder,
+    CASE
+        WHEN stock_available IS NULL OR reorder_level IS NULL THEN NULL
+        WHEN stock_available <= reorder_level THEN 'Yes'
+        ELSE 'No'
+    END AS needs_reorder,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CASE WHEN stock_available <= reorder_level THEN 'Yes' ELSE 'No' END AS needs_reorder,
CASE
WHEN stock_available IS NULL OR reorder_level IS NULL THEN NULL
WHEN stock_available <= reorder_level THEN 'Yes'
ELSE 'No'
END AS needs_reorder,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@script/silver/dbt_project/models/intermediate/Inventory/int_inventory_summary.sql`
at line 20, The current CASE expression for needs_reorder treats NULLs as 'No'
because comparisons with NULL yield UNKNOWN; update the logic in the SELECT
producing needs_reorder (referencing stock_available, reorder_level, and alias
needs_reorder) to explicitly preserve unknown by returning NULL when either
stock_available or reorder_level IS NULL, otherwise return 'Yes' if
stock_available <= reorder_level and 'No' otherwise.

warehouse_location,
store_id,
GETDATE() AS _loaded_at
FROM staging
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
-- Intermediate Model: int_products_standardized

WITH staging AS (
SELECT * FROM {{ ref('src_products') }}
)

SELECT
product_id,
sku,
product_name,
brand,
category,
sub_category,
department,
base_price_usd,
cost_price_usd,
gross_margin_pct,
CASE WHEN base_price_usd > 0 THEN (base_price_usd - cost_price_usd) / base_price_usd * 100 ELSE NULL END AS calculated_margin_pct,
weight_kg,
is_available,
stock_quantity,
reorder_level,
CASE WHEN stock_quantity <= reorder_level THEN 'Yes' ELSE 'No' END AS low_stock_flag,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show the exact section around the referenced line in the file
FILE="script/silver/dbt_project/models/intermediate/Products/int_products_standardized.sql"
echo "=== File: $FILE (lines around 23) ==="
if [ -f "$FILE" ]; then
  nl -ba "$FILE" | sed -n '1,120p' | sed -n '15,45p'
else
  echo "Missing file: $FILE"
fi

# 2) Find all occurrences of low_stock_flag and the specific CASE predicate
echo
echo "=== Search: low_stock_flag occurrences ==="
rg -n "low_stock_flag" script/silver/dbt_project/models || true

echo
echo "=== Search: stock_quantity <= reorder_level occurrences ==="
rg -n "stock_quantity\s*<=\s*reorder_level" script/silver/dbt_project/models || true

# 3) Search for a likely inventory model that may use similar logic
echo
echo "=== Search: reorder_level usage (context) ==="
rg -n "reorder_level" script/silver/dbt_project/models/intermediate script/silver/dbt_project/models/silver script/silver/dbt_project/models || true

# 4) Inspect any candidate inventory/stock-risk logic files around similar flags
echo
echo "=== Search: low stock / low_stock / stock risk related ==="
rg -n "(low_stock|low stock|stock[_ ]risk|reorder_level)" script/silver/dbt_project/models || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 257


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="script/silver/dbt_project/models/intermediate/Products/int_products_standardized.sql"
echo "=== Check file exists: $FILE ==="
ls -l "$FILE" || true

echo
echo "=== File: $FILE (lines 15-45 with line numbers) ==="
if [ -f "$FILE" ]; then
  awk 'NR>=15 && NR<=45 {printf "%d:%s\n", NR, $0}' "$FILE"
fi

echo
echo "=== Search: low_stock_flag occurrences ==="
rg -n "low_stock_flag" script/silver/dbt_project/models || true

echo
echo "=== Search: stock_quantity <= reorder_level occurrences ==="
rg -n "stock_quantity\s*<=\s*reorder_level" script/silver/dbt_project/models || true

echo
echo "=== Search: reorder_level usage (intermediate scope) ==="
rg -n "reorder_level" script/silver/dbt_project/models/intermediate || true

echo
echo "=== Search: stock risk / low stock related ==="
rg -n "(low_stock|low stock|stock[_ ]risk|reorder_level)" script/silver/dbt_project/models/intermediate || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 3089


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="script/silver/dbt_project/models/intermediate/Inventory/int_inventory_summary.sql"
echo "=== File: $FILE (lines 1-80) ==="
if [ -f "$FILE" ]; then
  awk 'NR>=1 && NR<=80 {printf "%d:%s\n", NR, $0}' "$FILE"
else
  echo "Missing file: $FILE"
fi

echo
echo "=== Search for IS NULL / NULL-safe CASE patterns near stock fields ==="
rg -n "(IS NULL|COALESCE|NULLIF).*stock_quantity|stock_quantity.*(IS NULL|COALESCE|NULLIF)" script/silver/dbt_project/models/intermediate || true
rg -n "(IS NULL|COALESCE|NULLIF).*reorder_level|reorder_level.*(IS NULL|COALESCE|NULLIF)" script/silver/dbt_project/models/intermediate || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 818


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="script/silver/dbt_project/models/marts/Dimensions/dim_products.sql"
echo "=== File: $FILE (lines 1-120) ==="
if [ -f "$FILE" ]; then
  awk 'NR>=1 && NR<=120 {printf "%d:%s\n", NR, $0}' "$FILE"
else
  echo "Missing file: $FILE"
fi

Repository: Ritik574-coder/dbt_learning_project

Length of output: 817


Avoid classifying NULL stock inputs as 'No'.

script/silver/dbt_project/models/intermediate/Products/int_products_standardized.sql line 23 uses CASE WHEN stock_quantity <= reorder_level THEN 'Yes' ELSE 'No' END, which will emit 'No' when either stock_quantity or reorder_level is NULL (NULL comparison → ELSE), understating low-stock risk.

💡 Proposed fix
-    CASE WHEN stock_quantity <= reorder_level THEN 'Yes' ELSE 'No' END AS low_stock_flag,
+    CASE
+        WHEN stock_quantity IS NULL OR reorder_level IS NULL THEN NULL
+        WHEN stock_quantity <= reorder_level THEN 'Yes'
+        ELSE 'No'
+    END AS low_stock_flag,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CASE WHEN stock_quantity <= reorder_level THEN 'Yes' ELSE 'No' END AS low_stock_flag,
CASE
WHEN stock_quantity IS NULL OR reorder_level IS NULL THEN NULL
WHEN stock_quantity <= reorder_level THEN 'Yes'
ELSE 'No'
END AS low_stock_flag,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@script/silver/dbt_project/models/intermediate/Products/int_products_standardized.sql`
at line 23, The low_stock_flag CASE treats NULL comparisons as ELSE 'No',
understating risk; update the logic in int_products_standardized.sql so
low_stock_flag returns 'Yes' when stock_quantity or reorder_level is NULL or
when stock_quantity <= reorder_level, and only returns 'No' when both values are
non-NULL and stock_quantity > reorder_level; change the CASE tied to columns
stock_quantity and reorder_level that produces low_stock_flag accordingly.

supplier_name,
supplier_country,
warranty_years,
rating_avg,
review_count,
CASE
WHEN rating_avg >= 4.5 THEN 'Excellent'
WHEN rating_avg >= 3.5 THEN 'Good'
WHEN rating_avg >= 2.5 THEN 'Average'
WHEN rating_avg >= 1.0 THEN 'Poor'
ELSE 'Not Rated'
END AS rating_category,
launched_date,
product_url,
GETDATE() AS _loaded_at
FROM staging
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- Intermediate Model: int_returns_summary

WITH staging AS (
SELECT * FROM {{ ref('src_returns') }}
)

SELECT
return_id,
original_txn_id,
original_order_id,
customer_id,
customer_name,
product_id,
product_name,
quantity_returned,
return_date,
return_reason,
refund_amount,
refund_method,
return_channel,
restocked,
return_status,
handled_by_emp_id,
notes,
DATEPART(YEAR, return_date) AS return_year,
DATEPART(MONTH, return_date) AS return_month,
GETDATE() AS _loaded_at
FROM staging
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- Intermediate Model: int_reviews_summary

WITH staging AS (
SELECT * FROM {{ ref('src_reviews') }}
)

SELECT
review_id,
txn_id,
customer_id,
customer_name,
product_id,
product_name,
rating,
rating_text,
review_date,
verified_purchase,
helpful_votes,
review_channel,
review_title,
DATEPART(YEAR, review_date) AS review_year,
DATEPART(MONTH, review_date) AS review_month,
GETDATE() AS _loaded_at
FROM staging
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- Intermediate Model: int_stores_standardized

WITH staging AS (
SELECT * FROM {{ ref('src_stores') }}
)

SELECT
store_id,
store_name,
store_type,
address,
city,
state,
state_full,
CONCAT(city, ', ', state) AS store_location,
zip_code,
country,
region,
district,
phone,
manager_name,
opened_date,
DATEDIFF(YEAR, opened_date, GETDATE()) AS years_operating,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the relevant section of the file
FILE="script/silver/dbt_project/models/intermediate/Stores/int_stores_standardized.sql"
echo "== File: $FILE =="
python3 - <<'PY'
import itertools,sys,os
path="script/silver/dbt_project/models/intermediate/Stores/int_stores_standardized.sql"
with open(path,'r',encoding='utf-8') as f:
    lines=f.readlines()
for i in range(15,35):
    if i<=len(lines):
        print(f"{i:4d}: {lines[i-1].rstrip()}")
PY

# Search for years_operating and similar DATEDIFF usage nearby/elsewhere
echo
echo "== rg: years_operating =="
rg -n "years_operating" script/silver/dbt_project/models || true

echo
echo "== rg: DATEDIFF\(YEAR, opened_date =="
rg -n "DATEDIFF\s*\(\s*YEAR\s*,\s*opened_date" script/silver/dbt_project/models || true

echo
echo "== rg: DATEDIFF\(YEAR, .* GETDATE\(\) =="
rg -n "DATEDIFF\s*\(\s*YEAR\s*,[^)]*GETDATE\(\)\s*\)" script/silver/dbt_project/models || true

# Look for employee tenure logic if it exists (same alleged bug pattern)
echo
echo "== rg: tenure / employment years =="
rg -n "(tenure|employee.*tenure|years.*tenure|employment.*years)" script/silver/dbt_project/models || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 2107


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print more context around opened_date usage and the rest of the model
FILE="script/silver/dbt_project/models/intermediate/Stores/int_stores_standardized.sql"
echo "== Context around opened_date in $FILE =="
python3 - <<'PY'
path="script/silver/dbt_project/models/intermediate/Stores/int_stores_standardized.sql"
with open(path,'r',encoding='utf-8') as f:
    lines=f.readlines()
for i,line in enumerate(lines, start=1):
    if 'opened_date' in line:
        start=max(1,i-5)
        end=min(len(lines),i+5)
        print(f"\n--- {path}:{start}-{end} (around line {i}) ---")
        for j in range(start,end+1):
            print(f"{j:4d}: {lines[j-1].rstrip()}")
PY

# Inspect employee tenure model for parallel pattern
EFILE="script/silver/dbt_project/models/intermediate/Employees/int_employees_standardized.sql"
echo
echo "== Context around hire_date / calculated_tenure_years in $EFILE =="
python3 - <<'PY'
path="script/silver/dbt_project/models/intermediate/Employees/int_employees_standardized.sql"
with open(path,'r',encoding='utf-8') as f:
    lines=f.readlines()
for i,line in enumerate(lines, start=1):
    if 'hire_date' in line or 'calculated_tenure_years' in line:
        start=max(1,i-5)
        end=min(len(lines),i+8)
        print(f"\n--- {path}:{start}-{end} (around line {i}) ---")
        for j in range(start,end+1):
            print(f"{j:4d}: {lines[j-1].rstrip()}")
PY

# Search for casts/conversions of opened_date anywhere else in Stores intermediate/marts
echo
echo "== rg 'opened_date' across Stores intermediate/marts =="
rg -n "opened_date" script/silver/dbt_project/models || true

# Search for TRY_CAST/CAST patterns on opened_date specifically
echo
echo "== rg cast/opened_date patterns =="
rg -n "(CAST|TRY_CAST|CONVERT)\s*\(\s*opened_date" script/silver/dbt_project/models || true

Repository: Ritik574-coder/dbt_learning_project

Length of output: 4526


Compute completed operating years (avoid anniversary overcount)

DATEDIFF(YEAR, opened_date, GETDATE()) in script/silver/dbt_project/models/intermediate/Stores/int_stores_standardized.sql counts crossed year boundaries and can overstate by 1 when a store hasn’t reached its anniversary yet.

💡 Proposed fix
-    DATEDIFF(YEAR, opened_date, GETDATE()) AS years_operating,
+    DATEDIFF(YEAR, opened_date, CAST(GETDATE() AS DATE))
+      - CASE
+          WHEN DATEADD(YEAR, DATEDIFF(YEAR, opened_date, CAST(GETDATE() AS DATE)), opened_date) > CAST(GETDATE() AS DATE)
+          THEN 1 ELSE 0
+        END AS years_operating,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
DATEDIFF(YEAR, opened_date, GETDATE()) AS years_operating,
DATEDIFF(YEAR, opened_date, CAST(GETDATE() AS DATE))
- CASE
WHEN DATEADD(YEAR, DATEDIFF(YEAR, opened_date, CAST(GETDATE() AS DATE)), opened_date) > CAST(GETDATE() AS DATE)
THEN 1 ELSE 0
END AS years_operating,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@script/silver/dbt_project/models/intermediate/Stores/int_stores_standardized.sql`
at line 23, The years_operating field overcounts when the current date is before
the store's anniversary; replace the simple DATEDIFF(YEAR, opened_date,
GETDATE()) logic with a two-step calculation: compute diff = DATEDIFF(YEAR,
opened_date, GETDATE()) and then subtract 1 if the anniversary this year (use
DATEADD to add diff years to opened_date) is after GETDATE(); update the
expression that defines years_operating to use DATEDIFF, DATEADD and a
conditional subtraction so stores haven't reached their anniversary yet are not
overcounted.

sq_footage,
num_employees,
annual_rent_usd,
CASE WHEN sq_footage > 0 THEN TRY_CAST(num_employees AS DECIMAL(10,2)) / sq_footage * 1000 ELSE NULL END AS employees_per_1000_sqft,
is_active,
has_parking,
has_cafe,
GETDATE() AS _loaded_at
FROM staging
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
-- Intermediate Model: int_transactions_enriched

WITH staging AS (

SELECT *
FROM {{ ref('src_transactions') }}

)

SELECT

transaction_id,
order_id,
order_line_number,

order_date,
order_year,
order_month,
order_month_name,
order_quarter,
order_day_of_week,

customer_id,
product_id,

-- Sales Metrics
TRY_CAST(quantity_ordered AS INT) AS quantity_ordered,

TRY_CAST(unit_list_price AS DECIMAL(18,2)) AS unit_list_price,

TRY_CAST(discount_pct AS DECIMAL(18,2)) AS discount_pct,

TRY_CAST(unit_selling_price AS DECIMAL(18,2)) AS unit_selling_price,

TRY_CAST(line_total_before_tax AS DECIMAL(18,2)) AS line_total_before_tax,

TRY_CAST(tax_rate_pct AS DECIMAL(18,2)) AS tax_rate_pct,

TRY_CAST(tax_amount AS DECIMAL(18,2)) AS tax_amount,

TRY_CAST(line_total_with_tax AS DECIMAL(18,2)) AS line_total_with_tax,

-- Derived Metrics
(
TRY_CAST(unit_selling_price AS DECIMAL(18,2))
*
TRY_CAST(quantity_ordered AS INT)
) AS gross_sales_amount,

TRY_CAST(cost_price AS DECIMAL(18,2)) AS cost_price,

TRY_CAST(gross_profit AS DECIMAL(18,2)) AS gross_profit,

(
TRY_CAST(line_total_with_tax AS DECIMAL(18,2))
-
TRY_CAST(cost_price AS DECIMAL(18,2))
) AS net_profit,

CASE
WHEN TRY_CAST(line_total_with_tax AS DECIMAL(18,2)) = 0 THEN NULL

ELSE ROUND(
(
(
TRY_CAST(line_total_with_tax AS DECIMAL(18,2))
-
TRY_CAST(cost_price AS DECIMAL(18,2))
)
/
TRY_CAST(line_total_with_tax AS DECIMAL(18,2))
) * 100,
2
)

END AS profit_margin_pct,

-- Dimensions
store_id,
employee_id,

promo_id,
promo_name,

LOWER(TRIM(sales_channel)) AS sales_channel,

payment_method,
shipping_method,
order_status,
is_returned,

-- Audit Column
GETDATE() AS _loaded_at

FROM staging
Loading