Importer en n'ajoutant que les objets qui n'existent pas encore

Bonjour,
j’ai fait beaucoup de tests avec les fichiers de liens xml pour en comprendre les règles, mais il y a toujours des points qui ne sont pas clairs (j’ai déjà lu la documentation présente ici de multiples fois mais rien ne précise les différentes utilisations des attributs des balises).

Une des fonctions que j’ai essayé de créer plusieurs fois est un import qui va créer les objets dont le code n’est pas présent dans Axelor, mais mettre à jour ceux qui le sont. Ce fichier xml a apporté le meilleur résultat :

<?xml version="1.0" encoding="UTF-8"?>
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.4.xsd">

    <input file="produits.csv" separator=";" type="com.axelor.apps.base.db.Product"/>

    <input file="produits.csv" separator=";" type="com.axelor.apps.base.db.Product" search="code = :code" update="true"/>

</csv-inputs>

mais le premier tag <input/> essaye d’ajouter des objets dont le code existe déjà, ce qui pose un problème de contrainte « UNIQUE » SQL pendant l’import et génère des tracebacks qui ralentissent l’import. Existe-t-il un moyen d’obtenir un meilleur résultat?

J’ai aussi un import plus complexe pour les nomenclatures (bill of material) qui pose le même problème : le traceback indique que l’import essaye de créer un produit dont le code est déjà utilisé, cependant, d’après ce que j’ai compris des attributs du tag <input/>, Axelor ne devrait que mettre à jour les objets existants. Voici le xml en question :

<?xml version="1.0" encoding="UTF-8"?>
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.4.xsd">

    <input file="nomenclatures.csv" separator=";" type="com.axelor.apps.production.db.BillOfMaterial">
        <bind column="name" to="name"/>
        <bind column="name" to="fullName"/>
        <bind to="product" search="self.code = :product">
            <bind column="product" to="code"/>
        </bind>
        <bind to="company" search="self.code = :company">
            <bind column="company" to="code"/>
        </bind>
        <bind column="qty" to="qty"/>
        <bind to="unit" search="self.name = :unit">
            <bind column="unit" to="name"/>
        </bind>
        <bind column="defineSubBillOfMaterial" to="defineSubBillOfMaterial"/>
        <bind eval="3" to="statusSelect"/>

        <bind to="prodProcess" if="!prodProcess.empty">
            <bind column="prodProcess" to="fullName"/>
            <bind column="prodProcess" to="name"/>
            <bind column="prodProcess" to="code"/>
            <bind to="product" search="self.code = :product">
                <bind column="product" to="code"/>
            </bind>
            <bind to="company" search="self.code = :company">
                <bind column="company" to="code"/>
            </bind>
            <bind to="outsourcing" eval="!subcontractor.empty"/>
            <bind eval="3" to="statusSelect"/>

            <bind to="subcontractors" search="self.description = :subcontractor"
                  if="!subcontractor.empty" update="true">
                <bind to="isSubcontractor" eval="true"/>
            </bind>

            <bind to="workshopStockLocation" search="self.name = :stockLocation" if="!stockLocation.empty">
                <bind column="stockLocation" to="name"/>
            </bind>

            <bind to="workshopStockLocation" search="self.name = 'Atelier inconnu'" if="stockLocation.empty">
                <bind eval="'Atelier inconnu'" to="name"/>
            </bind>

        </bind>

        <bind to="workshopStockLocation" search="self.name = :stockLocation" if="!stockLocation.empty">
            <bind column="stockLocation" to="name"/>
        </bind>

        <bind to="workshopStockLocation" search="self.name = 'Atelier inconnu'" if="stockLocation.empty">
            <bind eval="'Atelier inconnu'" to="name"/>
        </bind>

    </input>

    <input file="nomenclatures.csv" separator=";" type="com.axelor.apps.base.db.Product"
           search="self.code = :product" update="defineSubBillOfMaterial.equals('true')">
        <bind to="defaultBillOfMaterial" search="self.name = :name" if="defineSubBillOfMaterial.equals('true')"/>
        <bind to="unit" search="self.name = :unit">
            <bind column="unit" to="name"/>
        </bind>
    </input>

</csv-inputs>

Je suppose que cette erreur est générée par le fait que defineSubBillOfMaterial.equals("true") peut être false, mais la situation <input if="false"/> n’est pas documentée pour valider cette hypothèse.

EDIT : voici un des tracebacks générés par l’import des nomenclatures ci-dessus :

com.axelor.exception.AxelorException: The line cannot be imported (import : import nomenclatures)
	at com.axelor.apps.base.service.imports.listener.ImporterListener.handle(ImporterListener.java:85)
	at com.axelor.data.csv.CSVImporter.process(CSVImporter.java:325)
	at com.axelor.data.csv.CSVImporter.process(CSVImporter.java:226)
	at com.axelor.data.csv.CSVImporter.run(CSVImporter.java:198)
	at com.axelor.apps.base.service.imports.importer.ImporterCSV.process(ImporterCSV.java:37)
	at com.axelor.apps.base.service.imports.importer.Importer.run(Importer.java:113)
	at com.axelor.apps.base.service.imports.importer.Importer.run(Importer.java:120)
	at com.axelor.apps.base.service.imports.ImportService.run(ImportService.java:37)
	at com.axelor.apps.base.web.ImportConfigurationController.run(ImportConfigurationController.java:44)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.axelor.meta.ActionHandler.call(ActionHandler.java:251)
	at com.axelor.meta.schema.actions.ActionMethod.evaluate(ActionMethod.java:76)
	at com.axelor.meta.schema.actions.Action.execute(Action.java:100)
	at com.axelor.meta.schema.actions.Action.wrap(Action.java:109)
	at com.axelor.meta.schema.actions.ActionGroup.evaluate(ActionGroup.java:231)
	at com.axelor.meta.schema.actions.Action.execute(Action.java:96)
	at com.axelor.meta.schema.actions.Action.wrap(Action.java:109)
	at com.axelor.meta.ActionHandler.execute(ActionHandler.java:519)
	at com.axelor.meta.ActionExecutor.execute(ActionExecutor.java:47)
	at com.axelor.meta.ActionExecutor$$EnhancerByGuice$$6c24d2b3.CGLIB$execute$0(<generated>)
	at com.axelor.meta.ActionExecutor$$EnhancerByGuice$$6c24d2b3$$FastClassByGuice$$b0883d1e.invoke(<generated>)
	at com.google.inject.internal.cglib.proxy.$MethodProxy.invokeSuper(MethodProxy.java:228)
	at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:76)
	at com.axelor.rpc.ResponseInterceptor.invoke(ResponseInterceptor.java:56)
	at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:78)
	at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:54)
	at com.axelor.meta.ActionExecutor$$EnhancerByGuice$$6c24d2b3.execute(<generated>)
	at com.axelor.web.service.ActionService.execute(ActionService.java:107)
	at com.axelor.web.service.ActionService$$EnhancerByGuice$$6e3a19da.CGLIB$execute$0(<generated>)
	at com.axelor.web.service.ActionService$$EnhancerByGuice$$6e3a19da$$FastClassByGuice$$85164d8.invoke(<generated>)
	at com.google.inject.internal.cglib.proxy.$MethodProxy.invokeSuper(MethodProxy.java:228)
	at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:76)
	at com.axelor.rpc.RequestFilter.invoke(RequestFilter.java:55)
	at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:78)
	at com.axelor.rpc.ResponseInterceptor.invoke(ResponseInterceptor.java:65)
	at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:78)
	at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:54)
	at com.axelor.web.service.ActionService$$EnhancerByGuice$$6e3a19da.execute(<generated>)
	at sun.reflect.GeneratedMethodAccessor665.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:140)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:294)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:248)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:235)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:398)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:205)
	at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:228)
	at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
	at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:89)
	at com.axelor.db.tenants.AbstractTenantFilter.doFilter(AbstractTenantFilter.java:70)
	at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82)
	at org.apache.shiro.guice.web.SimpleFilterChain.doFilter(SimpleFilterChain.java:44)
	at io.buji.pac4j.filter.SecurityFilter.lambda$doFilter$0(SecurityFilter.java:86)
	at org.pac4j.core.engine.DefaultSecurityLogic.perform(DefaultSecurityLogic.java:140)
	at com.axelor.auth.pac4j.AuthPac4jModule$AxelorSecurityFilter$1.perform(AuthPac4jModule.java:484)
	at com.axelor.auth.pac4j.AuthPac4jModule$AxelorSecurityFilter$1.perform(AuthPac4jModule.java:468)
	at io.buji.pac4j.filter.SecurityFilter.doFilter(SecurityFilter.java:84)
	at org.apache.shiro.guice.web.SimpleFilterChain.doFilter(SimpleFilterChain.java:41)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
	at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
	at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82)
	at com.axelor.app.internal.AppFilter.doFilter(AppFilter.java:94)
	at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82)
	at com.google.inject.persist.PersistFilter.doFilter(PersistFilter.java:94)
	at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82)
	at com.axelor.db.tenants.AbstractTenantFilter.doFilter(AbstractTenantFilter.java:70)
	at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82)
	at com.axelor.web.servlet.CorsFilter.doFilter(CorsFilter.java:137)
	at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82)
	at com.google.inject.servlet.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:121)
	at com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:133)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:196)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:698)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:366)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:639)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:847)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1680)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:750)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
	at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:147)
	at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
	at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162)
	at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1441)
	at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1421)
	at com.axelor.db.JPA.persist(JPA.java:128)
	at com.axelor.db.JPA.manage(JPA.java:444)
	at com.axelor.data.csv.CSVImporter.importRow(CSVImporter.java:397)
	at com.axelor.data.csv.CSVImporter.process(CSVImporter.java:308)
	... 104 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
	at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:112)
	at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
	at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208)
	at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45)
	at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3003)
	at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3503)
	at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:89)
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:589)
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
	at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
	at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
	at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1435)
	... 109 more
Caused by: org.postgresql.util.PSQLException: ERREUR: la valeur d'une clé dupliquée rompt la contrainte unique « uk_kmoa50rib71d6kh82rgrd9rpb »
  Détail : La clé « (code)=(46658.SOUD) » existe déjà.
	at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2412)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2125)
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:297)
	at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:428)
	at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:354)
	at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:169)
	at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:136)
	at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
	at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
	at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:205)
	... 118 more

Note : Quand je cherche le produit 46658.SOUD dans Axelor, il n’existe pas. La nomenclature non-plus d’ailleurs.

HORS SUJET (toutes mes excuses pour ça)
Vos recherches m’intrigue, pourquoi le choix de l’import XML ?
Et pas rapport à l’API, vous n’en voulez pas ? Ne savez pas faire ? ou autre raison ?

Avec mon thread sur n8n (sur ce forum), ce que vous évoquez ne me semble pas très difficile. mais je n’ai pas encore fait d’essai. Je passerai du temps ce week end sur ce sujet, surtout si vous n’avez aucune autre réponse.

La recherche d’élements et leurs mises à jour de champs spécifiques … me semblent très facile avec l’app n8n.
La création d’élement avec juste les champs exigés semble s’autocompléter en API.

j’ai clairement besoin de faire beaucoup plus de test de mon côté pour affimer ce genre de choses.
J’espère revenir vers vous avec de bonnes nouvelles et vous donner les moyens de le faire très simplement.

Bonjour , pour faire des imports il faut utiliser le champ Importid, cette contraite empeche les doublons dans axelor et permet les mise a jour

Pour les update via API, je n’ai pas besoin de l’importId, l’id simple suffit
Mais il est impératif de donner le numéro de version pour éviter les conflits
https://docs.axelor.com/adk/5.2/dev-guide/web-services/rest.html#update-a-record

Oui par api il vous faut d abord faire une requête pour savoir s il est déjà en base , puis faire l update . Pour cela il vous faut constituer le body de la requête en Json . Bcp plus simple l import csv si on a les données sous format Excel .

L’id dans la table axelor est auto incrémenté , il ne doit jamais être présent dans les données que l on importe . Import id permet d avoir une intégrité au niveau des données sources ( par exemple avec l id dans la table des données qui doivent être importées . Pour mettre a jour suffit de changer les valeurs dans le fichier csv , l update va se faire automatiquement .

Excuse me for writing in english, this theme is very important for me. @bDurtaut how to get the next number of importId for a JSON file.
And what happend if I need to use the sequences with prefixes. Thank you for your comments and help.

Hello , no problem for english . ImportId is important if you want to import a csv file , in acolon of your csv file you make the value of the key of your data to export . I dont test add record or update record with a web service request .

@bDurtaut thanks for responding very quickly.

@POK vous avez progressé ?

@bDurtaut 100% d’accord.
La difficulté avec importid c’est de l’obtenir (d’une part) et de bien le conserver (d’auter part) pour mettre à jour les données. Donc pour @POK, l’importid n’est pas pertinent s’il veut créer uniquement ce qui n’existe pas encore ?!

[API mode only]
Depuis ce week end je travail sur un workflow pour :

  • lire un fichier csv (node 8 sur l’image en bas)
  • l’exploiter comme tableau (node 9)
  • boucler sur chaque ligne (node 10 sur l’image en bas)
  • vérifier la présence sur Axelor (en cours) - Je m’interroge sur combien de requêtes je fais : importid, id,libellé, code, autres
  • faire des conditions (en reflexion) - SI id=id, SI libellé « like » libellé, code=code …
  • action de création ou de modification (en reflexion) - trouver une astuce pour gérer tous les champs possibles.
  • Autres actions à réaliser selon vous ? Une potentielle vérification en récupérant les versions de chaque entrées modifiés ? Une différence entre le CSV entrant et le CSV sortant (très difficile) ? …

Le workflow boucle autant que necessaire sans probleme, la « fin » du workflow peut-être un simple message sur une messagerie locale (RocketChat sur l’image si dessous).

J’exporte sans probleme en csv tous les champs du domaines et toutes les données (node 4 à 7),
Donc un fichier exporté pourrait être modifié et utilisé comme fichier à importer.
À voir comment je m’assure que les tous les champs soient tous pris en compte pour la création et la mise à jour de chaque entrée.

Pour ceux qui veulent « voir » de quoi je parle :

Bonjour , très intéressant ce workflow n8n. En ce qui me concerne , pour faire de l intégration il y a deux phases , la première ou j importe les données en masses de l ancien système ( en gardant l intégrité grâce a ImportId comme je vous l ai écrit ) , l’id de mon ancien système est copié dans le champs ImportId des tables axelor . L’id des tables axelor est autoincrementé ( ne dois jamais figuré dans les fichiers de mapping d’import) .
La deuxième phase est en prod , si le système ancien est partiellement conservé , je code dans celui l appel a des WS axelor pour synchroniser les données.

Je suis en train de faire plein de tests.
Un produit « cassé » (qui n’a pas des champs suffisant après des modifications) disparait de l’interface web (mais tjs récupérable avec les exports).

C’est quelque choses que vous avez souvent vu ?
[EDIT : Surement lié à ma base de données, je ne suis pas conforme aux recommandations à ce niveau]


Je vais tenter une dynamique sur n8n afin de récupérer tous les champs d’un domaine (et donc toutes les colonnes possibles d’une entrée), et pas seulement de ceux de l’export qui n’affiche pas forcement toutes les colonnes.
Si j’arrive à faire cette dynamique, ca m’évitera de passer à la main et à chaque MAJ, les différentes colonnes attendues de Axelor, pour chaque domaine (fabrication, produits, devis …)
Reste à savoir si mon mappage depuis d’autre apps va être l’enfer ou non par la suite…

Si vous n utilisez pas l intégrité des données à importer , c est très très compliqué…chaque table ( devis par ex) a des colonnes qui font référence à d autre table ( foreign Key) . La aussi j utilise l.importid de la table de référence de l ancien système . Sinon il faut connaître l id de la table dans axelor .