Procházet zdrojové kódy

fix more cpr details

gdias před 3 týdny
rodič
revize
0b829cb307

+ 7 - 0
package-lock.json

@@ -11,6 +11,7 @@
 				"@schedule-x/calendar": "^3.3.0",
 				"@schedule-x/svelte": "^3.0.0",
 				"@schedule-x/theme-default": "^3.3.0",
+				"cidades-estados": "^1.4.1",
 				"flatpickr": "^4.6.13",
 				"layerchart": "^1.0.12",
 				"qr-code-styling": "^1.9.2",
@@ -1427,6 +1428,12 @@
 				"node": ">= 6"
 			}
 		},
+		"node_modules/cidades-estados": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/cidades-estados/-/cidades-estados-1.4.1.tgz",
+			"integrity": "sha512-wcByeWpvDGcWbGHsh8nmc7VXnA1BnJucn9MFoB/zXJUbyj+R56tHyYzfTzIorsIOFMPFySxwfHcveUc1oTv1uA==",
+			"license": "MIT"
+		},
 		"node_modules/clsx": {
 			"version": "2.1.1",
 			"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",

+ 1 - 0
package.json

@@ -29,6 +29,7 @@
 		"@schedule-x/calendar": "^3.3.0",
 		"@schedule-x/svelte": "^3.0.0",
 		"@schedule-x/theme-default": "^3.3.0",
+		"cidades-estados": "^1.4.1",
 		"flatpickr": "^4.6.13",
 		"layerchart": "^1.0.12",
 		"qr-code-styling": "^1.9.2",

+ 168 - 1
src/lib/components/commodities/cpr/EmissionCpr.svelte

@@ -1,4 +1,6 @@
 <script>
+  import { onMount } from 'svelte';
+
   export let formData = {};
   export let onFieldChange = () => {};
   export let requiredFields = new Set();
@@ -14,7 +16,41 @@
     { label: 'Não', value: 'N' }
   ];
 
+  const STATE_FIELD = 'cpr_deliveryPlace_state_acronym';
+  const CITY_FIELD = 'cpr_deliveryPlace_city_name';
+  const CIDADES_ESTADOS_SRC = 'https://cdn.jsdelivr.net/npm/cidades-estados@1.4.1/cidades-estados.js';
+  const isBrowser = typeof window !== 'undefined';
+
+  let estadosOptions = [];
+  let cidadesPorEstado = {};
+  let estadosCarregando = false;
+  let estadosErro = '';
+  let selectedStateValue = '';
+  let selectedCityValue = '';
+  let availableCities = [];
+
   const sections = [
+    {
+      title: 'Emissão e Entrega',
+      description: 'Local de entrega e prazos do documento.',
+      columns: 2,
+      fields: [
+        {
+          key: 'cpr_place_name',
+          label: 'Local de entrega',
+          type: 'select',
+          options: [
+            { label: 'Selecione...', value: '' },
+            { label: 'EX WORKS (NA FAZENDA EM PRODUÇÃO)', value: 'EX WORKS (NA FAZENDA EM PRODUÇÃO)' },
+            { label: 'ARMAZEM', value: 'ARMAZEM' }
+          ]
+        },
+        { key: 'cpr_document_deadline_days_number', label: 'Prazo para documentos (dias)' },
+        { key: STATE_FIELD, label: 'Estado da entrega' },
+        { key: CITY_FIELD, label: 'Cidade da entrega' },
+        // { key: 'cpr_deliveryPlace_ibge_code', label: 'IBGE da entrega' }
+      ]
+    },
     {
       title: 'Produto e lastro',
       description: 'Características do produto e da produção.',
@@ -45,6 +81,77 @@
     // }
   ];
 
+  onMount(() => {
+    if (!isBrowser) return;
+    hydrateCidadesEstadosOptions();
+  });
+
+  $: selectedStateValue = formData?.[STATE_FIELD] ?? '';
+  $: selectedCityValue = formData?.[CITY_FIELD] ?? '';
+  $: availableCities =
+    selectedStateValue && cidadesPorEstado[selectedStateValue]
+      ? cidadesPorEstado[selectedStateValue]
+      : [];
+
+  async function hydrateCidadesEstadosOptions() {
+    try {
+      estadosCarregando = true;
+      const ctor = await loadCidadesEstadosScript();
+      const proto = ctor?.prototype;
+      if (!proto?.estados || !proto?.cidades) {
+        throw new Error('Estrutura inválida da biblioteca cidades-estados.');
+      }
+
+      estadosOptions = [];
+      cidadesPorEstado = {};
+
+      proto.estados.forEach(([value, label], index) => {
+        if (!value) return;
+        estadosOptions.push({ value, label });
+        const cidades = proto.cidades?.[index] ?? [];
+        cidadesPorEstado[value] = cidades.filter(Boolean);
+      });
+      estadosErro = '';
+    } catch (error) {
+      console.error('Erro ao carregar cidades-estados:', error);
+      estadosErro = error?.message ?? 'Não foi possível carregar estados e cidades.';
+    } finally {
+      estadosCarregando = false;
+    }
+  }
+
+  function loadCidadesEstadosScript() {
+    if (!isBrowser) {
+      return Promise.reject(new Error('Ambiente indisponível para carregar cidades-estados.'));
+    }
+
+    if (window.__cidadesEstadosPromise) {
+      return window.__cidadesEstadosPromise;
+    }
+
+    window.__cidadesEstadosPromise = new Promise((resolve, reject) => {
+      if (window.dgCidadesEstados) {
+        resolve(window.dgCidadesEstados);
+        return;
+      }
+
+      const script = document.createElement('script');
+      script.src = CIDADES_ESTADOS_SRC;
+      script.async = true;
+      script.onload = () => {
+        if (window.dgCidadesEstados) {
+          resolve(window.dgCidadesEstados);
+        } else {
+          reject(new Error('Biblioteca cidades-estados não expôs dgCidadesEstados.'));
+        }
+      };
+      script.onerror = () => reject(new Error('Falha ao carregar biblioteca cidades-estados.'));
+      document.head.appendChild(script);
+    });
+
+    return window.__cidadesEstadosPromise;
+  }
+
   function getValue(key) {
     return formData?.[key] ?? '';
   }
@@ -60,6 +167,23 @@
       onRepeatingFieldChange(groupKey, index, fieldKey, event.currentTarget.value);
     };
   }
+
+  function isDeliveryStateField(key) {
+    return key === STATE_FIELD;
+  }
+
+  function isDeliveryCityField(key) {
+    return key === CITY_FIELD;
+  }
+
+  function handleDeliveryStateChange(value) {
+    onFieldChange(STATE_FIELD, value);
+    onFieldChange(CITY_FIELD, '');
+  }
+
+  function handleDeliveryCityChange(value) {
+    onFieldChange(CITY_FIELD, value);
+  }
 </script>
 
 <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm">
@@ -79,7 +203,50 @@
                   <span class="text-red-500">*</span>
                 {/if}
               </label>
-              {#if field.type === 'select'}
+
+              {#if isDeliveryStateField(field.key)}
+                <select
+                  class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-100 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-60"
+                  on:change={(event) => handleDeliveryStateChange(event.currentTarget.value)}
+                  bind:value={selectedStateValue}
+                  required={requiredFields?.has(field.key)}
+                  disabled={estadosCarregando || !!estadosErro}
+                >
+                  <option value="">{estadosCarregando ? 'Carregando estados...' : 'Selecione um estado'}</option>
+                  {#each estadosOptions as estado}
+                    <option value={estado.value}>{estado.label}</option>
+                  {/each}
+                </select>
+                {#if estadosErro}
+                  <p class="text-xs text-red-500 mt-1">{estadosErro}</p>
+                {/if}
+              {:else if isDeliveryCityField(field.key)}
+                <select
+                  class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-100 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-60"
+                  on:change={(event) => handleDeliveryCityChange(event.currentTarget.value)}
+                  bind:value={selectedCityValue}
+                  required={requiredFields?.has(field.key)}
+                  disabled={!selectedStateValue || !availableCities.length || !!estadosErro}
+                >
+                  <option value="">
+                    {#if !selectedStateValue}
+                      Selecione um estado primeiro
+                    {:else if estadosCarregando}
+                      Carregando cidades...
+                    {:else if !availableCities.length}
+                      Nenhuma cidade disponível
+                    {:else}
+                      Selecione uma cidade
+                    {/if}
+                  </option>
+                  {#each availableCities as cidade}
+                    <option value={cidade}>{cidade}</option>
+                  {/each}
+                </select>
+                {#if estadosErro}
+                  <p class="text-xs text-red-500 mt-1">{estadosErro}</p>
+                {/if}
+              {:else if field.type === 'select'}
                 <select
                   class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-100 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
                   value={getValue(field.key)}

+ 31 - 5
src/routes/cpr/+page.svelte

@@ -188,10 +188,27 @@
       addLabel: 'Adicionar emissor',
       columns: 2,
       fields: [
-        { key: 'cpr_issuer_name', label: 'Razão Social do Emissor' },
-        { key: 'cpr_issuers_document_number', label: 'Documento' },
-        { key: 'cpr_issuers_person_type_acronym', label: 'Tipo de pessoa' },
-        { key: 'cpr_issuer_legal_nature_code', label: 'Natureza jurídica' },
+        { key: 'cpr_issuer_name', label: 'Nome / (Razão Social do Emissor)' },
+        { key: 'cpr_issuers_document_number', label: 'Documento (CPF/CNPJ)' },
+        {
+          key: 'cpr_issuers_person_type_acronym',
+          label: 'Tipo de pessoa',
+          type: 'select',
+          options: [
+            { label: 'Selecione...', value: '' },
+            { label: 'Pessoa Jurídica (PJ)', value: 'PJ' },
+            { label: 'Pessoa Física (PF)', value: 'PF' }
+          ]
+        },
+        {
+          key: 'cpr_issuer_legal_nature_code',
+          label: 'Natureza jurídica',
+          type: 'select',
+          options: [
+            { label: 'Selecione...', value: '' },
+            { label: 'Produtor Rural', value: '02' }
+          ]
+        },
         { key: 'cpr_issuers_state_acronym', label: 'Estado' },
         { key: 'cpr_issuers_city_name', label: 'Cidade' }
       ]
@@ -204,7 +221,16 @@
       addLabel: 'Adicionar colateral',
       columns: 2,
       fields: [
-        { key: 'cpr_collateral_type_code', label: 'Código do colateral' },
+        {
+          key: 'cpr_collateral_type_code',
+          label: 'Código do colateral',
+          type: 'select',
+          options: [
+            { label: 'Selecione...', value: '' },
+            { label: 'Penhor', value: 'Penhor' },
+            { label: 'Alienação', value: 'Alienacao' }
+          ]
+        },
         { key: 'cpr_collateral_type_name', label: 'Descrição do colateral' },
         { key: 'cpr_constitution_process_indicator', label: 'Processo constituído', type: 'select', options: [
           { label: 'Selecione...', value: '' },