merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

lookup_inventory_products_filtered.c (9461B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2025 Taler Systems SA
      4 
      5    TALER is free software; you can redistribute it and/or modify it under the
      6    terms of the GNU General Public License as published by the Free Software
      7    Foundation; either version 3, or (at your option) any later version.
      8 
      9    TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13    You should have received a copy of the GNU General Public License along with
     14    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 /**
     17  * @file src/backenddb/lookup_inventory_products_filtered.c
     18  * @brief Lookup inventory product details for templates (filtered)
     19  * @author Bohdan Potuzhnyi
     20  */
     21 #include "platform.h"
     22 #include <taler/taler_pq_lib.h>
     23 #include "merchant-database/lookup_inventory_products_filtered.h"
     24 #include "helper.h"
     25 
     26 
     27 /**
     28  * Context used for TALER_MERCHANTDB_lookup_inventory_products_filtered().
     29  */
     30 struct LookupInventoryProductsContext
     31 {
     32   /**
     33    * Function to call with the results.
     34    */
     35   TALER_MERCHANTDB_InventoryProductCallback cb;
     36 
     37   /**
     38    * Closure for @a cb.
     39    */
     40   void *cb_cls;
     41 
     42   /**
     43    * Postgres context for callbacks.
     44    */
     45   struct TALER_MERCHANTDB_PostgresContext *pg;
     46 
     47   /**
     48    * Did database result extraction fail?
     49    */
     50   bool extract_failed;
     51 };
     52 
     53 
     54 static void
     55 lookup_inventory_products_cb (void *cls,
     56                               PGresult *result,
     57                               unsigned int num_results)
     58 {
     59   struct LookupInventoryProductsContext *plc = cls;
     60   struct TALER_MERCHANTDB_PostgresContext *pg = plc->pg;
     61 
     62   for (unsigned int i = 0; i < num_results; i++)
     63   {
     64     char *product_id;
     65     struct TALER_MERCHANTDB_InventoryProductDetails pd;
     66     size_t num_categories;
     67     uint64_t *categories;
     68     bool no_image_hash;
     69     struct GNUNET_PQ_ResultSpec rs[] = {
     70       GNUNET_PQ_result_spec_string ("product_id",
     71                                     &product_id),
     72       GNUNET_PQ_result_spec_string ("product_name",
     73                                     &pd.product_name),
     74       GNUNET_PQ_result_spec_string ("description",
     75                                     &pd.description),
     76       TALER_PQ_result_spec_json ("description_i18n",
     77                                  &pd.description_i18n),
     78       GNUNET_PQ_result_spec_string ("unit",
     79                                     &pd.unit),
     80       TALER_PQ_result_spec_array_amount_with_currency (pg->conn,
     81                                                        "merchant",
     82                                                        "price_array",
     83                                                        &pd.price_array_length,
     84                                                        &pd.price_array),
     85       GNUNET_PQ_result_spec_uint64 ("remaining_stock",
     86                                     &pd.remaining_stock),
     87       GNUNET_PQ_result_spec_uint32 ("remaining_stock_frac",
     88                                     &pd.remaining_stock_frac),
     89       TALER_PQ_result_spec_json ("taxes",
     90                                  &pd.taxes),
     91       GNUNET_PQ_result_spec_allow_null (
     92         GNUNET_PQ_result_spec_string ("image_hash",
     93                                       &pd.image_hash),
     94         &no_image_hash),
     95       GNUNET_PQ_result_spec_bool ("allow_fractional_quantity",
     96                                   &pd.allow_fractional_quantity),
     97       GNUNET_PQ_result_spec_uint32 ("fractional_precision_level",
     98                                     &pd.fractional_precision_level),
     99       GNUNET_PQ_result_spec_array_uint64 (pg->conn,
    100                                           "categories",
    101                                           &num_categories,
    102                                           &categories),
    103       GNUNET_PQ_result_spec_end
    104     };
    105 
    106     if (GNUNET_OK !=
    107         GNUNET_PQ_extract_result (result,
    108                                   rs,
    109                                   i))
    110     {
    111       GNUNET_break (0);
    112       plc->extract_failed = true;
    113       return;
    114     }
    115     plc->cb (plc->cb_cls,
    116              product_id,
    117              &pd,
    118              num_categories,
    119              categories);
    120     GNUNET_PQ_cleanup_result (rs);
    121   }
    122 }
    123 
    124 
    125 enum GNUNET_DB_QueryStatus
    126 TALER_MERCHANTDB_lookup_inventory_products_filtered (
    127   struct TALER_MERCHANTDB_PostgresContext *pg,
    128   const char *instance_id,
    129   const char *const *product_ids,
    130   size_t num_product_ids,
    131   const uint64_t *categories,
    132   size_t num_categories,
    133   TALER_MERCHANTDB_InventoryProductCallback cb,
    134   void *cb_cls)
    135 {
    136   struct LookupInventoryProductsContext plc = {
    137     .cb = cb,
    138     .cb_cls = cb_cls,
    139     .pg = pg,
    140     /* Can be overwritten by the lookup_inventory_products_cb */
    141     .extract_failed = false,
    142   };
    143   struct GNUNET_PQ_QueryParam params[] = {
    144     (0 == num_product_ids)
    145     ? GNUNET_PQ_query_param_null ()
    146     : GNUNET_PQ_query_param_array_ptrs_string (
    147       num_product_ids,
    148       (const char **) product_ids,
    149       pg->conn),
    150     (0 == num_categories)
    151     ? GNUNET_PQ_query_param_null ()
    152     : GNUNET_PQ_query_param_array_uint64 (num_categories,
    153                                           categories,
    154                                           pg->conn),
    155     GNUNET_PQ_query_param_end
    156   };
    157   enum GNUNET_DB_QueryStatus qs;
    158 
    159   GNUNET_assert (NULL != pg->current_merchant_id);
    160   GNUNET_assert (0 == strcmp (instance_id,
    161                               pg->current_merchant_id));
    162   TMH_PQ_prepare_anon (pg,
    163                        "SELECT"
    164                        " description"
    165                        ",description_i18n::TEXT"
    166                        ",product_name"
    167                        ",unit"
    168                        ",price_array"
    169                        ",CASE WHEN minv.total_stock = 9223372036854775807"
    170                        "      THEN minv.total_stock"
    171                        "      ELSE (GREATEST(0, rt.remaining_total) / 1000000)::INT8"
    172                        "      END AS remaining_stock"
    173                        ",CASE WHEN minv.total_stock = 9223372036854775807"
    174                        "      THEN 2147483647"
    175                        "      ELSE mod(GREATEST(0, rt.remaining_total), 1000000)::INT4"
    176                        "      END AS remaining_stock_frac"
    177                        ",taxes::TEXT"
    178                        ",image_hash"
    179                        ",allow_fractional_quantity"
    180                        ",fractional_precision_level"
    181                        ",product_id"
    182                        ",t.category_array AS categories"
    183                        " FROM merchant_inventory minv"
    184                        ",LATERAL ("
    185                        "   SELECT ARRAY ("
    186                        "     SELECT mpc.category_serial"
    187                        "       FROM merchant_product_categories mpc"
    188                        "      WHERE mpc.product_serial = minv.product_serial"
    189                        "   ) AS category_array"
    190                        " ) t"
    191                        ",LATERAL ("
    192                        "   SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000"
    193                        "                        + total_locked_frac::NUMERIC), 0)"
    194                        "          AS total_locked"
    195                        "     FROM merchant_inventory_locks mil"
    196                        "    WHERE mil.product_serial = minv.product_serial"
    197                        " ) il"
    198                        ",LATERAL ("
    199                        "   SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000"
    200                        "                        + total_locked_frac::NUMERIC), 0)"
    201                        "          AS total_locked"
    202                        "     FROM merchant_order_locks mol"
    203                        "    WHERE mol.product_serial = minv.product_serial"
    204                        " ) ol"
    205                        ",LATERAL ("
    206                        "   SELECT"
    207                        "     (minv.total_stock::NUMERIC * 1000000"
    208                        "      + minv.total_stock_frac::NUMERIC)"
    209                        "     - (minv.total_sold::NUMERIC * 1000000"
    210                        "        + minv.total_sold_frac::NUMERIC)"
    211                        "     - (minv.total_lost::NUMERIC * 1000000"
    212                        "        + minv.total_lost_frac::NUMERIC)"
    213                        "     - il.total_locked"
    214                        "     - ol.total_locked"
    215                        "     AS remaining_total"
    216                        " ) rt"
    217                        " WHERE ("
    218                        "     (COALESCE (array_length ($1::TEXT[], 1), 0) > 0"
    219                        "      AND minv.product_id = ANY ($1::TEXT[]))"
    220                        "     OR"
    221                        "     (COALESCE (array_length ($2::BIGINT[], 1), 0) > 0"
    222                        "      AND EXISTS ("
    223                        "        SELECT 1"
    224                        "          FROM merchant_product_categories mpc"
    225                        "         WHERE mpc.product_serial = minv.product_serial"
    226                        "           AND mpc.category_serial = ANY ($2::BIGINT[])"
    227                        "      ))"
    228                        "   )");
    229   qs = GNUNET_PQ_eval_prepared_multi_select (
    230     pg->conn,
    231     "",
    232     params,
    233     &lookup_inventory_products_cb,
    234     &plc);
    235   GNUNET_PQ_cleanup_query_params_closures (params);
    236   /* If there was an error inside lookup_inventory_products_cb, return a hard error. */
    237   if (plc.extract_failed)
    238     return GNUNET_DB_STATUS_HARD_ERROR;
    239   return qs;
    240 }