diff --git a/app/code/Shopkeeper/PDFingPO/Plugin/Amasty/.gitkeep b/app/code/Shopkeeper/PDFingPO/Plugin/Amasty/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/code/Shopkeeper/PDFingPO/Plugin/Amasty/FilterPlugin.php b/app/code/Shopkeeper/PDFingPO/Plugin/Amasty/FilterPlugin.php
new file mode 100644
index 0000000..9523e7e
--- /dev/null
+++ b/app/code/Shopkeeper/PDFingPO/Plugin/Amasty/FilterPlugin.php
@@ -0,0 +1,104 @@
+productRepository = $productRepository;
+ $this->logger = $logger;
+ }
+
+ /**
+ * Add variables before filtering
+ */
+ public function beforeFilter($subject, $value)
+{
+ if (!$this->costVariablesAdded) {
+ // Get existing variables
+ $variables = [];
+ if (method_exists($subject, 'getVariables')) {
+ $variables = $subject->getVariables();
+ } else {
+ // Fallback to reflection
+ try {
+ $reflection = new \ReflectionObject($subject);
+ $propNames = ['_templateVars', 'templateVars', 'variables']; // Common property names
+ foreach ($propNames as $propName) {
+ if ($reflection->hasProperty($propName)) {
+ $prop = $reflection->getProperty($propName);
+ $prop->setAccessible(true);
+ $variables = $prop->getValue($subject);
+ break;
+ }
+ }
+ } catch (\Exception $e) {
+ $this->logger->info('FilterPlugin: Could not access variables: ' . $e->getMessage());
+ }
+ }
+
+ if (isset($variables['order']) && $variables['order'] instanceof Order) {
+ $this->logger->info('FilterPlugin: Adding cost variables for order ' . $variables['order']->getIncrementId());
+
+ $order = $variables['order'];
+ $itemsHtml = '';
+ $totalCost = 0;
+
+ foreach ($order->getAllVisibleItems() as $item) {
+ try {
+ $product = $this->productRepository->getById($item->getProductId());
+ $cost = (float)($product->getCost() ?: $product->getData('cost') ?: 0);
+ } catch (\Exception $e) {
+ $cost = 0;
+ }
+
+ $qty = (float) $item->getQtyOrdered();
+ $rowTotal = $cost * $qty;
+ $totalCost += $rowTotal;
+
+ $itemsHtml .= '
';
+ $itemsHtml .= '| ' . htmlspecialchars($item->getName()) . ' | ';
+ $itemsHtml .= '' . htmlspecialchars($item->getSku()) . ' | ';
+ $itemsHtml .= '' . (int)$qty . ' | ';
+ $itemsHtml .= '' . $this->formatPrice($cost, $order) . ' | ';
+ $itemsHtml .= '' . $this->formatPrice($rowTotal, $order) . ' | ';
+ $itemsHtml .= '
';
+ }
+
+ $shippingCost = $order->getShippingAmount() ?: 0;
+
+ // Add new variables to the array
+ $variables['items_html'] = $itemsHtml;
+ $variables['order_subtotal_cost'] = $this->formatPrice($totalCost, $order);
+ $variables['order_total_cost'] = $this->formatPrice($totalCost + $shippingCost, $order);
+ $variables['shipping_cost'] = $this->formatPrice($shippingCost, $order);
+ $variables['raw_subtotal_cost'] = $totalCost;
+ $variables['raw_total_cost'] = $totalCost + $shippingCost;
+ $variables['raw_shipping_cost'] = $shippingCost;
+
+ // Set the updated variables array
+ $subject->setVariables($variables);
+
+ $this->costVariablesAdded = true;
+ $this->logger->info('FilterPlugin: Cost variables added successfully');
+ }
+ }
+
+ return [$value];
+}
+
+ protected function formatPrice($price, $order)
+ {
+ return $order->getOrderCurrency()->formatPrecision($price, 2, [], false);
+ }
+}
diff --git a/app/code/Shopkeeper/PDFingPO/Plugin/Amasty/TemplatePlugin.php b/app/code/Shopkeeper/PDFingPO/Plugin/Amasty/TemplatePlugin.php
new file mode 100644
index 0000000..f4a667d
--- /dev/null
+++ b/app/code/Shopkeeper/PDFingPO/Plugin/Amasty/TemplatePlugin.php
@@ -0,0 +1,170 @@
+productRepository = $productRepository;
+ $this->logger = $logger;
+ }
+
+ /**
+ * Add cost variables before processing template
+ */
+ public function beforeProcessTemplate($subject, array $variables = [])
+ {
+ if (!$this->isProcessing) {
+ $this->logger->info('TemplatePlugin: beforeProcessTemplate called');
+
+ // Check if we have an order in variables
+ if (isset($variables['order']) && $variables['order'] instanceof Order) {
+ $this->logger->info('TemplatePlugin: Found order ' . $variables['order']->getIncrementId());
+ $variables = $this->addCostVariables($variables, $variables['order']);
+ }
+ }
+
+ return [$variables];
+ }
+
+ /**
+ * Hook into setVars to add our variables
+ */
+ public function afterSetVars($subject, $result, $variables)
+ {
+ // Only process once to avoid infinite loop
+ if (!$this->isProcessing && is_array($variables) && isset($variables['order']) && $variables['order'] instanceof Order) {
+ $this->isProcessing = true;
+
+ $this->logger->info('TemplatePlugin: afterSetVars called - adding cost variables');
+
+ // Add cost variables
+ $enhancedVariables = $this->addCostVariables($variables, $variables['order']);
+
+ // Set variables back
+ $subject->setVars($enhancedVariables);
+
+ $this->isProcessing = false;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Add cost variables to the variables array
+ */
+ protected function addCostVariables($variables, $order)
+ {
+ // Check if we already added these variables
+ if (isset($variables['items_html'])) {
+ $this->logger->info('Cost variables already exist, skipping');
+ return $variables;
+ }
+
+ try {
+ // Build ONLY the table rows (tbody content)
+ $itemsHtml = '';
+ $totalCost = 0;
+ $itemCount = 0;
+
+ foreach ($order->getAllVisibleItems() as $item) {
+ $itemCount++;
+ try {
+ $product = $this->productRepository->getById($item->getProductId());
+ $cost = (float)($product->getCost() ?: $product->getData('cost') ?: 0);
+
+ $this->logger->info('Product ' . $item->getSku() . ' cost: ' . $cost);
+ } catch (\Exception $e) {
+ $this->logger->error('Could not load product: ' . $e->getMessage());
+ $cost = 0;
+ }
+
+ $qty = (float) $item->getQtyOrdered();
+ $rowTotal = $cost * $qty;
+ $totalCost += $rowTotal;
+
+ // Just the row content
+ $itemsHtml .= '';
+ $itemsHtml .= '| ' . htmlspecialchars($item->getName()) . ' | ';
+ $itemsHtml .= '' . htmlspecialchars($item->getSku()) . ' | ';
+ $itemsHtml .= '' . (int)$qty . ' | ';
+ $itemsHtml .= '' . $this->formatPrice($cost, $order) . ' | ';
+ $itemsHtml .= '' . $this->formatPrice($rowTotal, $order) . ' | ';
+ $itemsHtml .= '
';
+ }
+
+ $shippingCost = $order->getShippingAmount() ?: 0;
+
+ // Create a complete table HTML for standalone use
+ $completeTableHtml = '';
+ $completeTableHtml .= '';
+ $completeTableHtml .= '';
+ $completeTableHtml .= '| Item | ';
+ $completeTableHtml .= 'SKU | ';
+ $completeTableHtml .= 'Qty | ';
+ $completeTableHtml .= 'Unit Cost | ';
+ $completeTableHtml .= 'Total Cost | ';
+ $completeTableHtml .= '
';
+ $completeTableHtml .= '';
+ $completeTableHtml .= '';
+ $completeTableHtml .= $itemsHtml;
+ $completeTableHtml .= '';
+ $completeTableHtml .= '';
+ $completeTableHtml .= '';
+ $completeTableHtml .= '| Subtotal: | ';
+ $completeTableHtml .= '' . $this->formatPrice($totalCost, $order) . ' | ';
+ $completeTableHtml .= '
';
+ if ($shippingCost > 0) {
+ $completeTableHtml .= '';
+ $completeTableHtml .= '| Shipping: | ';
+ $completeTableHtml .= '' . $this->formatPrice($shippingCost, $order) . ' | ';
+ $completeTableHtml .= '
';
+ }
+ $completeTableHtml .= '';
+ $completeTableHtml .= '| Grand Total: | ';
+ $completeTableHtml .= '' . $this->formatPrice($totalCost + $shippingCost, $order) . ' | ';
+ $completeTableHtml .= '
';
+ $completeTableHtml .= '';
+ $completeTableHtml .= '
';
+
+ $terms = $order->getData('terms') ?? ''; // Or $order->getTerms() if it's a getter
+ $termsHtml = str_replace(',', '
', htmlspecialchars($terms));
+
+ // Add variables for both approaches
+ $variables['items_html'] = $itemsHtml; // Just rows for inserting into existing table
+ $variables['items_complete_table'] = $completeTableHtml; // Complete table
+ $variables['order_subtotal_cost'] = $this->formatPrice($totalCost, $order);
+ $variables['order_total_cost'] = $this->formatPrice($totalCost + $shippingCost, $order);
+ $variables['shipping_cost'] = $this->formatPrice($shippingCost, $order);
+ $variables['raw_subtotal_cost'] = $totalCost;
+ $variables['raw_total_cost'] = $totalCost + $shippingCost;
+ $variables['raw_shipping_cost'] = $shippingCost;
+ $variables['terms_html'] = $termsHtml;
+
+ // Test variable
+ $variables['test_cost_injection'] = 'WORKING';
+
+ $this->logger->info('Added cost variables - Items: ' . $itemCount . ', Total: ' . $totalCost);
+
+ } catch (\Exception $e) {
+ $this->logger->error('Error adding cost variables: ' . $e->getMessage());
+ }
+
+ return $variables;
+ }
+
+ protected function formatPrice($price, $order)
+ {
+ return $order->getOrderCurrency()->formatPrecision($price, 2, [], false);
+ }
+}
diff --git a/app/code/Shopkeeper/PDFingPO/composer.json b/app/code/Shopkeeper/PDFingPO/composer.json
new file mode 100644
index 0000000..d614880
--- /dev/null
+++ b/app/code/Shopkeeper/PDFingPO/composer.json
@@ -0,0 +1,15 @@
+{
+ "name": "shopkeeper/module-pdfing-po",
+ "description": "Display product costs in purchase order PDFs (PDFingPO)",
+ "type": "magento2-module",
+ "version": "1.0.0",
+ "license": "proprietary",
+ "autoload": {
+ "files": [
+ "registration.php"
+ ],
+ "psr-4": {
+ "Shopkeeper\\PDFingPO\\": ""
+ }
+ }
+}
diff --git a/app/code/Shopkeeper/PDFingPO/etc/.gitkeep b/app/code/Shopkeeper/PDFingPO/etc/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/code/Shopkeeper/PDFingPO/etc/di.xml b/app/code/Shopkeeper/PDFingPO/etc/di.xml
new file mode 100644
index 0000000..9e5377e
--- /dev/null
+++ b/app/code/Shopkeeper/PDFingPO/etc/di.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Shopkeeper/PDFingPO/etc/module.xml b/app/code/Shopkeeper/PDFingPO/etc/module.xml
new file mode 100644
index 0000000..a3e8f9f
--- /dev/null
+++ b/app/code/Shopkeeper/PDFingPO/etc/module.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Shopkeeper/PDFingPO/registration.php b/app/code/Shopkeeper/PDFingPO/registration.php
new file mode 100644
index 0000000..c6073ef
--- /dev/null
+++ b/app/code/Shopkeeper/PDFingPO/registration.php
@@ -0,0 +1,8 @@
+