diff --git a/src/NFe/Core/Nota.php b/src/NFe/Core/Nota.php index d688cb2..a463efb 100644 --- a/src/NFe/Core/Nota.php +++ b/src/NFe/Core/Nota.php @@ -45,6 +45,7 @@ use NFe\Exception\ValidationException; use FR3D\XmlDSig\Adapter\AdapterInterface; use FR3D\XmlDSig\Adapter\XmlseclibsAdapter; +use NFe\Entity\Cobranca; /** * Classe base para a formação da nota fiscal @@ -201,6 +202,13 @@ abstract class Nota implements Node */ private $pagamentos; + /** + * Cobrancas realizadas + * + * @var Cobranca[] + */ + private $cobrancas; + /** * Data e Hora da saída ou de entrada da mercadoria / produto */ @@ -559,6 +567,37 @@ public function addPagamento($pagamento) return $this; } + /** + * Cobranças realizadas + * @return mixed cobranças da Nota + */ + public function getCobrancas() + { + return $this->cobrancas; + } + + /** + * Altera o valor da Cobrança para o informado no parâmetro + * @param mixed $cobrancas novo valor para Cobranças + * @return self + */ + public function setCobrancas($cobrancas) + { + $this->cobrancas = $cobrancas; + return $this; + } + + /** + * Adiciona um(a) Cobrança para a lista de cobranças + * @param Cobranca $cobranca Instância da Cobrança que será adicionada + * @return self + */ + public function addCobranca($cobranca) + { + $this->cobrancas[] = $cobranca; + return $this; + } + /** * Data e Hora da saída ou de entrada da mercadoria / produto * @param boolean $normalize informa se a data_movimentacao deve estar no formato do XML @@ -1397,6 +1436,16 @@ public function toArray($recursive = false) } else { $nota['pagamentos'] = $this->getPagamentos(); } + if ($recursive) { + $cobrancas = []; + $_cobrancas = $this->getCobrancas(); + foreach ($_cobrancas as $_cobranca) { + $cobrancas[] = $_cobranca->toArray($recursive); + } + $nota['cobrancas'] = $cobrancas; + } else { + $nota['cobrancas'] = $this->getCobrancas(); + } $nota['data_movimentacao'] = $this->getDataMovimentacao($recursive); $nota['data_contingencia'] = $this->getDataContingencia($recursive); $nota['justificativa'] = $this->getJustificativa(); @@ -1467,6 +1516,11 @@ public function fromArray($nota = []) } else { $this->setPagamentos($nota['pagamentos']); } + if (!isset($nota['cobrancas'])) { + $this->setCobrancas([]); + } else { + $this->setCobrancas($nota['cobrancas']); + } if (isset($nota['data_movimentacao'])) { $this->setDataMovimentacao($nota['data_movimentacao']); } else { @@ -1811,7 +1865,16 @@ public function getNode($name = null) $transporte = $this->getTransporte()->getNode(); $transporte = $dom->importNode($transporte, true); $info->appendChild($transporte); - // TODO: adicionar cobrança + $_cobrancas = $this->getCobrancas(); + if (count($_cobrancas ?: []) > 0) { + $cobr = $dom->createElement('cobr'); + foreach ($_cobrancas as $_cobranca) { + $cobranca = $_cobranca->getNode(); + $cobranca = $dom->importNode($cobranca, true); + $cobr->appendChild($cobranca); + } + $info->appendChild($cobr); + } $pag = $dom->createElement('pag'); $_pagamentos = $this->getPagamentos(); foreach ($_pagamentos as $_pagamento) { @@ -2084,6 +2147,26 @@ public function loadNode($element, $name = null) } } $this->setPagamentos($pagamentos); + + $cobrancas = []; + $_items = $info->getElementsByTagName('cobr'); + foreach ($_items as $_item) { + $_det_items = $_item->getElementsByTagName('fat'); + foreach ($_det_items as $_det_item) { + $cobranca = new Cobranca(); + $cobranca->loadNode($_det_item, 'fat'); + $cobrancas[] = $cobranca; + } + $_det_items = $_item->getElementsByTagName('dup'); + foreach ($_det_items as $_det_item) { + $cobranca = new Cobranca(); + $cobranca->loadNode($_det_item, 'dup'); + $cobrancas[] = $cobranca; + } + } + $this->setCobrancas($cobrancas); + + $_fields = $info->getElementsByTagName('total'); if ($_fields->length > 0) { $total = new Total(); diff --git a/src/NFe/Entity/Cobranca.php b/src/NFe/Entity/Cobranca.php new file mode 100644 index 0000000..ea081a5 --- /dev/null +++ b/src/NFe/Entity/Cobranca.php @@ -0,0 +1,381 @@ + + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +namespace NFe\Entity; + +use NFe\Common\Node; +use NFe\Common\Util; + +class Cobranca implements Node +{ + /** + * Tipos de cobrança 0 - fatura; 1 - duplicata + */ + public const TIPO_FATURA = 'fatura'; + public const TIPO_DUPLICATA = 'duplicata'; + + /** + * Numero da cobrança + * + * @var string + */ + private $numero; + + /** + * Valor total da cobrança + * + * @var float + */ + private $valor; + + /** + * valor do desconto da cobrança + * + * @var float + */ + private $desconto; + + /** + * Valor liquido da cobrança + * + * @var float + */ + private $valor_liquido; + + /** + * Data de vencimento da cobrança + */ + private $vencimento; + + /** + * Tipo da cobrança + */ + private $tipo; + + /** + * Numero da cobrança + * @param boolean $normalize informa se a valor deve estar no formato do XML + * @return string|string valor of cobranca + */ + public function getNumero($normalize = false) + { + if (!$normalize) { + return $this->numero; + } + return $this->numero; + } + + /** + * Altera o numero da cobrança para o informado no parâmetro + * + * @param string|string|null $numero Novo Numero de Fatura para cobranca + * + * @return self A própria instância da classe + */ + public function setNumero($numero) + { + $this->numero = $numero; + return $this; + } + + /** + * Valor da cobranca + * @param boolean $normalize informa se a valor deve estar no formato do XML + * @return float|string valor of cobranca + */ + public function getValor($normalize = false) + { + if (!$normalize) { + return $this->valor; + } + return Util::toCurrency($this->valor); + } + + /** + * Altera o valor da Cobrança para o informado no parâmetro + * + * @param float|string|null $valor Novo valor de Fatura para cobranca + * + * @return self A própria instância da classe + */ + public function setValor($valor) + { + $valor = floatval($valor); + $this->valor = $valor; + return $this; + } + + /** + * Valor de desconto cobranca + * @param boolean $normalize informa se a valor deve estar no formato do XML + * @return float|string valor of cobranca + */ + public function getDesconto($normalize = false) + { + if (!$normalize) { + return $this->desconto; + } + return Util::toCurrency($this->desconto); + } + + /** + * Altera o valor do desconto da Fatura para o informado no parâmetro + * + * @param float|string|null $desconto Novo valor de desconto para cobranca + * + * @return self A própria instância da classe + */ + public function setDesconto($desconto) + { + $desconto = floatval($desconto); + $this->desconto = $desconto; + return $this; + } + + /** + * Valor liquido do cobranca + * @param boolean $normalize informa se a valor deve estar no formato do XML + * @return float|string valor of cobranca + */ + public function getValorLiquido($normalize = false) + { + if (!$normalize) { + return $this->valor_liquido; + } + return Util::toCurrency($this->valor_liquido); + } + + /** + * Altera o valor do liquido da Fatura para o informado no parâmetro + * + * @param float|string|null $valor_liquido Novo valor liquido para cobranca + * + * @return self A própria instância da classe + */ + public function setValorLiquido($valor_liquido) + { + $valor_liquido = floatval($valor_liquido); + $this->valor_liquido = $valor_liquido; + return $this; + } + + /** + * Vencimento do cobranca + * @param boolean $normalize informa se a valor deve estar no formato do XML + * @return float|string valor of cobranca + */ + public function getVencimento($normalize = false) + { + if (!$normalize) { + return $this->vencimento; + } + return $this->vencimento; + } + + /** + * Altera a data de vencimento da duplicata para o informado no parâmetro + * + * @param float|string|null $vencimento Novo vencimento da duplicata para cobranca + * + * @return self A própria instância da classe + */ + public function setVencimento($vencimento) + { + $this->vencimento = $vencimento; + return $this; + } + + /** + * Tipo da cobrança 0 – fatura; 1 – duplicata + * + * @param boolean $normalize informa se o indicador deve estar no formato do XML + * @return mixed tipo da cobrança + */ + public function getTipo($normalize = false) + { + if (!$normalize) { + return $this->tipo; + } + switch ($this->tipo) { + case self::TIPO_FATURA: + return '0'; + case self::TIPO_DUPLICATA: + return '1'; + } + return $this->tipo; + } + + /** + * Altera o valor do tipo para o informado no parâmetro + * @param mixed $tipo novo valor para tipo + * @return self A própria instância da classe + */ + public function setTipo($tipo) + { + switch ($tipo) { + case '0': + $tipo = self::TIPO_FATURA; + break; + case '1': + $tipo = self::TIPO_DUPLICATA; + break; + } + $this->tipo = $tipo; + return $this; + } + + /** + * Constroi uma instância de cobranca vazia + * @param array $cobranca Array contendo dados do cobranca + */ + public function __construct($cobranca = []) + { + $this->fromArray($cobranca); + } + + public function toArray($recursive = false) + { + $cobranca = []; + $cobranca['tipo'] = $this->getTipo(); + $cobranca['numero'] = $this->getNumero(); + $cobranca['valor'] = $this->getValor(); + $cobranca['desconto'] = $this->getDesconto(); + $cobranca['valor_liquido'] = $this->getValorLiquido(); + $cobranca['vencimento'] = $this->getVencimento(); + return $cobranca; + } + + public function fromArray($cobranca = []) + { + if ($cobranca instanceof Cobranca) { + $cobranca = $cobranca->toArray(); + } elseif (!is_array($cobranca)) { + return $this; + } + if (isset($cobranca['tipo'])) { + $this->setTipo($cobranca['tipo']); + } else { + $this->setTipo(null); + } + if (isset($cobranca['numero'])) { + $this->setNumero($cobranca['numero']); + } else { + $this->setNumero(null); + } + if (isset($cobranca['valor'])) { + $this->setValor($cobranca['valor']); + } else { + $this->setValor(null); + } + if (isset($cobranca['desconto'])) { + $this->setDesconto($cobranca['desconto']); + } else { + $this->setDesconto(null); + } + if (isset($cobranca['valor_liquido'])) { + $this->setValorLiquido($cobranca['valor_liquido']); + } else { + $this->setValorLiquido(null); + } + if (isset($cobranca['vencimento'])) { + $this->setVencimento($cobranca['vencimento']); + } else { + $this->setVencimento(null); + } + return $this; + } + + public function getNode($name = null) + { + // TODO: implementar a inserção da cobranca no xml + throw new \Exception('NÃO IMPLEMENTADO', 404); + } + + public function loadNode($element, $name = null) + { + $name = is_null($name) ? 'dup' : $name; + + if ($element->nodeName != $name) { + $_fields = $element->getElementsByTagName($name); + if ($_fields->length == 0) { + throw new \Exception('Tag "' . $name . '" não encontrada', 404); + } + $element = $_fields->item(0); + } + $this->setTipo( + $name == 'fat' ? 0 : 1 + ); + if ($this->getTipo() == self::TIPO_FATURA) { + $this->setNumero( + Util::loadNode( + $element, + 'nFat' + ) + ); + $this->setValor( + Util::loadNode( + $element, + 'vOrig' + ) + ); + } else { + $this->setNumero( + Util::loadNode( + $element, + 'nDup' + ) + ); + $this->setValor( + Util::loadNode( + $element, + 'vDup' + ) + ); + } + $this->setDesconto( + Util::loadNode( + $element, + 'vDesc' + ) + ); + $this->setValorLiquido( + Util::loadNode( + $element, + 'vLiq' + ) + ); + $this->setVencimento( + Util::loadNode( + $element, + 'dVenc' + ) + ); + return $element; + } +} diff --git a/src/NFe/Entity/Endereco.php b/src/NFe/Entity/Endereco.php index f8c31f3..5479e84 100644 --- a/src/NFe/Entity/Endereco.php +++ b/src/NFe/Entity/Endereco.php @@ -161,7 +161,8 @@ public function parseDescricao($descricao) { $pattern = '/(.*), (.*) - (.*)/'; if (!preg_match($pattern, $descricao, $matches)) { - throw new \Exception('Não foi possível desmembrar a linha de endereço', 500); + $this->setLogradouro($descricao); + return $this; } $this->setLogradouro($matches[1]); $this->setNumero($matches[2]); @@ -282,8 +283,7 @@ public function loadNode($element, $name = null) $this->setBairro( Util::loadNode( $element, - 'xBairro', - 'Tag "xBairro" do campo "Bairro" não encontrada' + 'xBairro' ) ); $this->getMunicipio()->setCodigo( diff --git a/src/NFe/Entity/Total.php b/src/NFe/Entity/Total.php index d786c13..f1bccf7 100644 --- a/src/NFe/Entity/Total.php +++ b/src/NFe/Entity/Total.php @@ -76,6 +76,11 @@ class Total implements Node */ private $complemento; + /** + * valor total da nota + */ + private $valor; + /** * Constroi uma instância de Total vazia * @param array $total Array contendo dados do Total @@ -274,6 +279,30 @@ public function setComplemento($complemento) return $this; } + /** + * Valor total da nota + * @param boolean $normalize informa se o complemento deve estar no formato do XML + * @return mixed Valor total + */ + public function getValor($normalize = false) + { + if (!$normalize) { + return $this->valor; + } + return Util::toCurrency($this->valor); + } + + /** + * Altera o valor do total da nota + * @param mixed $valor novo valor para Total da Nota + * @return self A própria instância da classe + */ + public function setValor($valor) + { + $this->valor = $valor; + return $this; + } + /** * Converte a instância da classe para um array de campos com valores * @return array Array contendo todos os campos e valores da instância @@ -287,6 +316,7 @@ public function toArray($recursive = false) $total['frete'] = $this->getFrete(); $total['despesas'] = $this->getDespesas(); $total['tributos'] = $this->getTributos(); + $total['total'] = $this->getValor(); $total['complemento'] = $this->getComplemento(); return $total; } @@ -333,6 +363,11 @@ public function fromArray($total = []) } else { $this->setTributos($total['tributos']); } + if (!array_key_exists('total', $total)) { + $this->setValor(null); + } else { + $this->setValor($total['total']); + } if (!array_key_exists('complemento', $total)) { $this->setComplemento(null); } else { @@ -366,6 +401,9 @@ public function getNode($name = null) if (!is_null($this->getTributos())) { Util::appendNode($element, 'vTotTrib', $this->getTributos(true)); } + if (!is_null($this->getValor())) { + Util::appendNode($element, 'vNF', $this->getValor(true)); + } if (! empty($this->getComplemento())) { Util::appendNode($element, 'infCpl', $this->getComplemento(true)); } @@ -400,6 +438,7 @@ public function loadNode($element, $name = null) $this->setFrete(Util::loadNode($element, 'vFrete')); $this->setDespesas(Util::loadNode($element, 'vOutro')); $this->setTributos(Util::loadNode($element, 'vTotTrib')); + $this->setValor(Util::loadNode($element, 'vNF')); $this->setComplemento(Util::loadNode($element, 'infCpl')); return $element; } diff --git a/src/NFe/Entity/Transporte/Transportador.php b/src/NFe/Entity/Transporte/Transportador.php index 4fee211..15960ed 100644 --- a/src/NFe/Entity/Transporte/Transportador.php +++ b/src/NFe/Entity/Transporte/Transportador.php @@ -97,9 +97,6 @@ public function loadNode($element, $name = null) } $cnpj = Util::loadNode($element, 'CNPJ'); $cpf = Util::loadNode($element, 'CPF'); - if (is_null($cnpj) && is_null($cpf)) { - throw new \Exception('Tag "CNPJ" ou "CPF" não encontrada no Transportador', 404); - } $this->setCNPJ($cnpj); $this->setCPF($cpf); if (! empty($this->getCNPJ())) { @@ -122,8 +119,7 @@ public function loadNode($element, $name = null) $this->setIE( Util::loadNode( $element, - 'IE', - 'Tag "IE" do campo "IE" não encontrada' + 'IE' ) ); $this->setIM(null); @@ -137,15 +133,13 @@ public function loadNode($element, $name = null) $endereco->getMunicipio()->setNome( Util::loadNode( $element, - 'xMun', - 'Tag "xMun" do nome do município não encontrada' + 'xMun' ) ); $endereco->getMunicipio()->getEstado()->setUF( Util::loadNode( $element, - 'UF', - 'Tag "UF" da UF do estado não encontrada' + 'UF' ) ); $this->setEndereco($endereco);